12 Commits
Author SHA1 Message Date
Juan DenisandClaude Opus 5 ac4d3bf227 feat(recycle-bin): Recycle Bin UI under Settings > Admin
One list of everything the panel has soft-deleted, with Restore on every row
and a quiet purge for admins. Type-agnostic: `kind` comes from the server's
registry, so a newly restorable model appears here with no change to this
component.

Says out loud what restore does NOT do -- it returns the record, not the
configuration torn down when it was deleted -- because a Recycle Bin that
implies otherwise is worse than none.

The retention countdown per row ("purges in 3d") turns amber in the last
stretch: that is the only warning before a record is gone for good. Purge is
admin-only, sits behind a confirm, and stays a dim icon next to a full-width
Restore -- the safety net should make the safe action the loud one.

Registered as a settings tab with search-index entries, so the command palette
finds "restore", "undelete", "trash".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 07:25:00 -04:00
Juan DenisandClaude Opus 5 c66d5037ef feat(ds): DataGrid -- the list surface for pages where the table is the page
Ported from the Domains design mock. A CSS grid, not a <table>: header and
rows share one --sk-grid-tpl track list, which is what makes show/hide and
move-left/right a pure data change with no <colgroup> to keep in sync.

  fields.js         field model + filter-rule engine (text/enum/num/bool/date),
                    sort, group, CSV/JSON export
  DataGrid          grid, sticky head, groups, selection, density, sub-detail
  ColumnMenu        per-column popover: sort / group / filter / move / hide.
                    Flat, state-model-agnostic props so <DataTable> can drive
                    it from sorts[]+hiddenKeys and <DataGrid> from one cfg.
  useGridConfig     the whole chrome as ONE serialisable object, so a saved
                    view is a snapshot of it and "dirty" is a deep-equal away
  useGridRows       filter -> sort -> page -> group, memoised
  GridViewPicker    saved views rendered as the page heading
  GridFilterDrawer  Filters + Fields panes
  GridChips / GridBulkBar / GridFooter / GridToolsMenu / GridFilterButton

Does not replace ds/DataTable -- column descriptors are a superset, so a
list can move up by adding `type` and `width`.

Adds the --surface-0 token (the recessed step below --surface) that group
headers, drawer tab strips and drawer footers sit on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 05:52:05 -04:00
Juan DenisandClaude Opus 5 ce3420f607 refactor(monitoring): one group for Monitors, Incidents, Events and Jobs
The group's information architecture had drifted. The Events tab pointed at
/telemetry, which was nested inside the monitoring TabGroupLayout but ALSO
rendered its own PageTopbar — two stacked headers. Jobs sat as a top-level
sidebar entry while being the same class of thing as Events, so the two read as
rival pages.

- MONITOR_TABS is now Overview / Monitors / Incidents / Rules / Capacity /
  Events / Jobs / Doctor (+ Status Pages from the extension). Alerts is gone as
  a tab; /monitoring/alerts and /monitoring/fleet-alerts redirect to Incidents,
  and FleetAlertsPanel moved there with its own ack/resolve lifecycle intact.
- Jobs moves to /monitoring/jobs and loses its sidebar entry; /jobs redirects.
  Activity/Scheduled becomes an in-page SegControl since the group's bar already
  owns the tab row. JOBS_TABS is deleted.
- Events (Telemetry) rebuilt on the standard pattern: no PageTopbar of its own,
  KpiBand + MetricCard severity filters instead of bespoke telemetry-stat divs,
  topbar SearchField + FilterDrawer instead of an inline collapsible panel,
  DataTable + DS Drawer. Every capability is kept — correlation drill-down, all
  filters, test-event emit, cleanup, pagination.
- The Overview leads with a monitors summary (availability, live check feed,
  monitor list); host resources keep everything they had, one section down.
- Status pages can now publish a monitor that already exists, and detach it
  again without destroying it — deleting a component now deletes a real monitor.

FilterDrawer grows `children` + `onClear` so a host can add non-list filters
(free text, date bounds) and still have "Clear all" reach them; without onClear
it would silently reset only the chip groups.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 00:03:27 -04:00
Juan DenisandClaude Opus 5 85359c83eb refactor(nav): dev-gate the Test Sandbox, move Webhooks into Settings
Two sidebar surfaces that shouldn't have been top-level pages. Both
touch App.jsx + sidebarItems.js, so they land together.

Test Sandbox: the distro-matrix console shipped visible and routable
on every production install. Add a shared useDevMode() hook
(module-scope cache + listeners, same shape as useModules) resolving
`import.meta.env.DEV || (isAdmin && dev_mode)`. The sidebar item gates
on requiresCondition: 'devMode' and the route on the same hook, so a
visible nav entry can never redirect to the dashboard. The guard waits
on `resolved` first, or an admin deep link would bounce before the
dev_mode fetch lands. Settings' toggle calls refreshDevMode() so the
sidebar reacts without a reload.

Webhooks: the inbound receive/verify/forward console is server
configuration, not a daily ops surface, so it becomes a Settings ->
Admin tab next to the outbound subscriptions it complements. /webhooks
and /secrets/webhooks redirect there; the sidebar item is gone, which
empties ADVANCED_ITEM_IDS down to the queue console. The command
palette entry drops its navId (a Settings tab has no nav item for the
workspace permission gate to consult) and settingsIndex.js gains the
three entries check-settings-index.mjs requires for a new tab. The
native confirm() on delete becomes the shared useConfirm() dialog.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 22:44:28 -04:00
Juan DenisandClaude Opus 5 b413148194 feat(monitoring): fold Fleet Monitor in, and scope the page to a server
Fleet Monitor and /monitoring were the same questions at two scopes, so they
were two pages restating each other: both grew an alerts surface and both
grew thresholds. They are now one page with a scope, not two pages with a
gap between them.

The top bar carries a server picker, URL-backed as ?server= so the choice
survives a tab change, a refresh and a pasted link. Overview, Alerts and
Capacity all follow it. The fourth KPI tile is whichever reading the current
scope actually has — load average on the panel host, container count on an
agent — rather than a blank where the other one would be.

The overview is ported from the design mock: KPI band with trends and
sparklines, two trend charts, and a Host health strip below. That strip is
what the standalone heatmap became, except each card is a button that
re-scopes everything above it instead of a cell you can only read.

There is deliberately no second tab strip inside the page. Overview / Alerts
/ Rules / Capacity / Doctor live in the group's own top bar, the way the mock
puts them — a nav under a nav reads as two competing headers.

Also: the sidebar said "Observability" for a page, a route and a title that
all said "Monitoring", and the delivery channels rendered Discord, Slack and
a raw webhook with the same generic glyph. Both fixed.

/fleet-monitor redirects; the Servers tab strip loses the entry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:03:15 -04:00
Juan DenisandClaude Fable 5 7d4543312c feat(themes): add 11 bundled Venezuelan seed themes
Turpial, Araguaney, Orquídea, Apamate, Ceiba, Azulejo, Guacamaya, Cacao,
Papelón, Onoto (dual dark+light) and Tepuy (radius override) — named after
Venezuelan national symbols and nature. Authored in
frontend/src/data/themes/, synced to backend via sync-bundled-themes.mjs
(17 bundled themes total). check-theme-tokens + backend theme suites pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 10:22:23 -04:00
Juan DenisandClaude Opus 4.8 0016b1ff22 feat(themes): theme gallery + runtime skins (plan 60 Phase 1)
A ServerKit theme is data, not code: a validated map of CSS custom-property
tokens. This lands the client-side foundation.

- themeTokens.js: canonical token whitelist, legacy-alias table, and strict
  value validators (reject url()/@/;/}/expression, type-check per group)
- applySkin.js: runtime skin applier via setProperty (injection-safe); dark/
  light resolution with stock-light fallback for dark-only themes
- ThemeContext: per-user skin selection (localStorage), live hover preview,
  and accent precedence workspace > user > skin > default
- 6 bundled seed themes (default/paper/nord-deep/gruvbox/phosphor/high-contrast)
- Theme Gallery in Settings > Appearance (instant apply, 4-swatch cards) +
  command-palette index entry
- hardcoded-hex audit: 3 exact token-value leaks fixed to var(--token)
- check-theme-tokens.mjs proving guard wired into the lint chain

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 23:55:19 -04:00
Juan DenisandClaude Opus 4.8 9a1a1f69a3 feat: configurable panel title + switchable login layouts
Make the panel's identity admin-configurable instead of hardcoded, and let
operators pick a login-page layout — so instances can be branded/varied from
the panel rather than by editing source.

- Settings: new `panel_title` (default "ServerKit") and `login_layout`
  (default "centered") keys via SettingsService (key/value — no migration).
- Exposed pre-auth on the existing GET /auth/setup-status so the login page
  reads them before any authenticated call.
- Frontend threads panelTitle + loginLayout through AuthContext; document.title
  (App.jsx PageTitleUpdater + usePageTitle) and the auth-page headings use the
  configured title, with import.meta.env.VITE_PANEL_TITLE as the build-time
  default for the first paint.
- Login page refactored to an AuthLayout selector with three variants —
  Centered (current), Split-hero, Minimal — chosen by `login_layout`.
- Admin UI: a "Panel Appearance" card (title field + layout dropdown) in the
  admin-only Site Settings tab; saves via the generic settings API and calls
  refreshSetupStatus so the running SPA updates live. Searchable via a new
  settingsIndex entry.

Register/Setup adopt the configured title on their headings. manifest.json
stays static (documented limitation). Verified: eslint clean on all touched
files (0 errors), settings-index guard passes. (The repo's full `vite build`
currently fails on an unrelated pre-existing plugin import, serverkit-tramo →
`tramo/react`, independent of this change.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:01:39 -04:00
Juan DenisandClaude Opus 4.8 76a2788d24 refactor(ui): rename Marketplace to Extensions
"Marketplace" reads as a store; the page is where you browse and install
extensions, so call it Extensions.

- Nav label + page/tab title + route /marketplace -> /extensions
  (first tab drives the topbar title via TabGroupLayout).
- /marketplace and /marketplace/installed now redirect to /extensions.
- Update command palette, page-context map, NotFound, empty-state copy.
- Keep the internal nav id, CSS classes, component/file names, and the
  backend /api/v1/marketplace/registry endpoints unchanged; palette keeps
  "marketplace" as a search keyword so the old term still finds it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 02:37:17 -04:00
Juan DenisandClaude Opus 4.8 3e0fbd2f81 feat(palette): omnisearch core — providers, ranking, VS Code keys
Rebuild CommandPalette around scored providers (pages, settings, actions,
plugin contributions, docs, async entities) with a single subsequence fuzzy
scorer and shouldFilter={false} so cmdk no longer double-filters. Adds:

- data/palettePages.js, data/settingsIndex.js (84 cards), data/commandActions.js
- utils/paletteScore.js (word-start bonus + category weight) and
  utils/paletteFrecency.js (localStorage frecency: count + time decay)
- hooks/usePaletteAuthz.js — mirrors the sidebar's admin + workspace-nav gate
  so a member's palette never surfaces admin surface
- F1 and Ctrl/Cmd+Shift+P open aliases alongside Ctrl/Cmd+K
- prefix modes (`>` actions, `?` docs), Recently used empty state, kbd footer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:31:25 -04:00
Juan DenisandClaude Opus 4.8 bd87b06a60 feat(new-service): a real three-step wizard, not a form-wall
/services/new rendered everything at once regardless of state — a "Ready to
Import" rail of placeholder rows, a decorative Connect->Detect->Deploy strip, a
numbered guide, and two boilerplate note boxes. It is now a slim stepper over
one centered column.

- three explicit steps: Source (radio-cards) -> Connect (source-specific input
  + API-driven template picker) -> Review (manifest detection, buildpack, env
  list, core fields; nothing renders without real data)
- decomposed the 1,327-line monolith into pages/new-service/: a
  useNewServiceForm hook (step state + submit) with presentational SourceStep /
  ConnectStep / ReviewStep components
- ?template=/?source= deep links land preloaded on the Connect step; deploy
  templates load from the backend catalog (kind: repo) instead of a JS constant
- submit payloads are byte-identical to before (flow re-skin, not a backend
  change); _new-service.scss rewritten for the stepper layout
- deletes the hardcoded frontend catalog (data/deployTemplates.js) now that its
  last consumer is gone, and the old single-file NewService.jsx

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 02:39:23 -04:00
Juan DenisandClaude Opus 4.8 6accbf9655 feat(services): New Service wizard clarity — guides, shared templates, docs links
- Per-source explainer strip (SOURCE_GUIDES): the selected source card renders
  what it does, what to have ready, and the 2-3 next steps.
- Extract the hardcoded SERVICE_TEMPLATES into frontend/src/data/deployTemplates.js;
  the Templates tab gains a 'Deploy templates' section (badged 'Example' for the
  demo) with a 'Use template' deep-link (/services/new?template=<id>) that
  preselects it in the wizard. One list, two entry points; Template Library
  button now targets #deploy-templates.
- New docsLinks.js map + <DocsLink> (book icon, opens serverkit.ai docs in a new
  tab, hidden under White Label — the single place that rule lives). Wired into
  the New Service source pane (deploy-sources / manifest) and the Marketplace
  manual-install modal (extensions/installing).

Lint 0 errors; build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 02:12:24 -04:00