rustploy-gui 0.1.0

Rustploy — desktop client (glacier-ui) for the Rustploy PaaS daemon
// Janela de LOGS AO VIVO (runtime OU build): motor Glacier próprio e ISOLADO do
// app principal, aberta via open_window por handlers/services.luau
// (open_logs_window / open_build_logs_window).
screen {
  resources {
    style """
      /* Alças de resize (moldura de 6px), iguais ao app.gv (janela borderless). */
      .rz_top    { width: fill; height: 6; }
      .rz_bottom { width: fill; height: 6; }
      .rz_mid    { width: fill; height: fill; }
      .rz_body   { width: fill; height: fill; }
      .rz_corner { width: 6; height: fill; }
      .rz_h      { width: fill; height: fill; cursor: resize-v; }
      .rz_v      { width: 6; height: fill; cursor: resize-h; }
      .rz_nw     { cursor: resize-nw; }
      .rz_ne     { cursor: resize-ne; }
      .rz_sw     { cursor: resize-sw; }
      .rz_se     { cursor: resize-se; }
      /* Titlebar custom (mesma linguagem visual do app.gv). */
      .titlebar       { width: fill; height: 32; background: var(--surface); align-y: center; }
      .titlebar_drag  { width: fill; height: fill; align-y: center; padding: 0 0 0 16; spacing: 9; }
      .titlebar_dot   { size: 10; color: var(--blue); }
      .titlebar_title { size: 12; bold: true; color: var(--muted); }
      .win_btn        { width: 48; padding: 5 0; color: var(--border); text-align: center; }
      .win_close      { width: 48; padding: 5 0; color: var(--danger); text-align: center; }

      .lw_body   { width: fill; height: fill; padding: 14; spacing: 10; }
      .lw_head   { width: fill; align-y: center; spacing: 12; }
      .lw_title  { size: 13; bold: true; color: var(--text); }
      .lw_count  { width: fill; size: 11; color: var(--muted); }
      .lw_scroll { width: fill; height: fill; background: var(--surface); border-radius: 4; padding: 10; }
      .lw_editor { width: fill; height: fill; background: var(--surface); border-radius: 4; padding: 10 12; font: mono; }
    """

    script(src = "scripts/log_window.luau")
  }

  column(class = screen) {
    // Borda superior + cantos (n / nw / ne)
    row(class = rz_top) {
      column(class = "rz_corner rz_nw", on_press = window:resize:nw)
      column(class = rz_h, on_press = window:resize:n)
      column(class = "rz_corner rz_ne", on_press = window:resize:ne)
    }

    // Banda central: borda esquerda | corpo | borda direita (w / e)
    row(class = rz_mid) {
      column(class = rz_v, on_press = window:resize:w)

      column(class = rz_body) {
        // Titlebar custom (borderless)
        row(class = titlebar) {
          row(
            class = titlebar_drag,
            on_press = window:drag,
            on_double_click = window:maximize,
          ) {
            text(class = titlebar_dot, content = "●")
            text(class = titlebar_title, content = "@lw_title")
          }

          button(class = win_btn, on_click = window:minimize, text = "—")
          button(class = win_btn, on_click = window:maximize, text = "▢")
          button(class = win_close, on_click = window:close, text = "✕")
        }

        column(class = lw_body) {
          row(class = lw_head) {
            text(class = lw_title, content = "@lw_title")
            text(class = lw_count, content = "@lw_count linhas · ao vivo")

            button(
              class = tab,
              on_click = textarea_top:lw_logs_text,
              text = "↑",
              tooltip = "Ir ao topo",
            )

            button(
              class = tab,
              on_click = textarea_end:lw_logs_text,
              text = "↓",
              tooltip = "Ir ao fim",
            )

            button(
              class = tab,
              on_click = clipboard:lw_logs_text,
              text = "Copiar tudo",
            )
          }

          textarea(
            class = lw_editor,
            font = mono,
            readonly = true,
            :value = lw_logs_text,
            placeholder = "(sem logs ainda — aguardando o serviço emitir saída)",
          )
        }
      }

      column(class = rz_v, on_press = window:resize:e)
    }

    // Borda inferior + cantos (s / sw / se)
    row(class = rz_bottom) {
      column(class = "rz_corner rz_sw", on_press = window:resize:sw)
      column(class = rz_h, on_press = window:resize:s)
      column(class = "rz_corner rz_se", on_press = window:resize:se)
    }
  }
}