Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: CoreBunch/Instatic
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.0.4
Choose a base ref
...
head repository: CoreBunch/Instatic
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.0.5
Choose a head ref
  • 12 commits
  • 150 files changed
  • 2 contributors

Commits on Jun 13, 2026

  1. fix(publisher): render current entry body in any content outlet (#62)

    A base.outlet only rendered the post body when the node carried a
    persisted dynamicBindings.html overlay, which was set in exactly one
    place: the auto-seeded default template. A hand-dropped outlet (custom
    template) had no binding, so the publisher filled it with nothing and it
    rendered as an empty <main data-instatic-content-region></main> — on the
    published page, the content preview, and the Live Canvas alike.
    
    An outlet is, by definition, the hole the current entry's body flows
    into; there is no UI to bind it and the binding is always the same. Apply
    it implicitly via effectiveNodeBindings() at the shared resolution layer
    so every outlet renders the body, and drop the now-redundant persisted
    binding from the seed (single source of truth). Outside an entry route
    the entry stack is empty, so the binding stays inert and an everywhere
    layout's outlet still hosts a whole page.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    DavidBabinec and claude authored Jun 13, 2026
    Configuration menu
    Copy the full SHA
    b9ee769 View commit details
    Browse the repository at this point in the history

Commits on Jun 16, 2026

  1. fix(canvas): render base.text tag:none as bare text to match publishe…

    …d DOM (#63)
    
    * fix(canvas): render base.text tag:none as bare text to match published DOM
    
    A `base.text` node with `tag: none` publishes as bare text (no element) but
    the canvas wrapped it in a `<span>`. Descendant selectors such as
    `.ist-404__num span` then matched the phantom span in the editor while leaving
    the published page untouched — e.g. the 404 template's two `tag: none` "4"s
    rendered orange in the builder but black on the published page.
    
    The canvas now renders `tag: none` as bare text + `<br>` breaks, exactly like
    the publisher (`src/modules/base/text/index.ts`), so the canvas DOM equals the
    published DOM. This is the same canvas-equals-published principle that drove
    removing the per-node wrapper `<div>`.
    
    Trade-off: a `tag: none` node owns no element, so it has no in-canvas selection
    ring / hover / inline-edit; it is selected via the Layers panel and edited via
    the Properties "Text" field. The inline-edit branch still hosts a `<span>` for
    an active session, which is unreachable from the canvas for `tag: none` (no
    element to double-click).
    
    Verified live in the editor: the 404 template's "4"s now render black and the
    "0" orange, matching the front end; `.ist-404__num` DOM is `4<span>0</span>4`.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    * refactor(canvas): extract BareText component for tag:none rendering
    
    Satisfies react-doctor/no-render-in-render: a named component returning a
    fragment replaces the inline render helper. Still adds no host element, so the
    tag:none canvas DOM remains bare text matching the published page.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    DavidBabinec and claude authored Jun 16, 2026
    Configuration menu
    Copy the full SHA
    1a762ec View commit details
    Browse the repository at this point in the history
  2. fix(export): make the export size estimate server-accurate, including…

    … media (#64)
    
    The "Estimated size" line in the Export dialog was a client-side guess that
    ignored media entirely (`mediaAssetCount` was hardcoded to 0) and underestimated
    content ~10x (flat 8 KB shell + 1.5 KB/row constants). Toggling "Media files"
    never moved the number, which read as "media isn't exported" — but media *is*
    exported correctly; only the estimate was wrong.
    
    Replace the heuristic with an exact, server-computed estimate that reuses the
    export's own selection logic as a single source of truth:
    
    - New `POST /admin/api/cms/export/estimate` runs the identical table/row/media
      selection as the real export but computes the byte size instead of streaming
      the bundle. It never reads media files off disk or base64-encodes them:
      it serializes the real selection with empty `bytesBase64` strings and adds
      each asset's analytic Base64 length (4*ceil(n/3)). Because Base64 is ASCII,
      this equals the real bundle byte length exactly.
    - `export.ts` is refactored to share `buildBundle` + media-metadata mapping
      between the download and estimate paths, so they cannot drift.
    - `useExportEstimate` now fetches this endpoint (debounced, cancellable) and
      formats the result; the dialog mirrors the exact ExportRequest it will send.
    
    Verified live against a real site (19 images, ~69 MB): estimate without media =
    183,618 B, with media = 96,769,329 B — byte-identical to the actual download.
    Tests assert estimate === real download size for the no-media, no-shell, and
    embedded-media cases.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    DavidBabinec and claude authored Jun 16, 2026
    Configuration menu
    Copy the full SHA
    2eb0999 View commit details
    Browse the repository at this point in the history

Commits on Jun 17, 2026

  1. docs(readme): rewrite the GitHub front page in a human voice with dee…

    …per feature detail (#69)
    
    Replace the AI-flavored marketing copy with plainer, more human prose and
    expand the feature coverage. Highlights:
    
    - New anti-stack opening; drops the WordPress-vs-Webflow framing.
    - Deploy section is now a provider table (Railway recommended; Render,
      Fly.io, DigitalOcean marked coming soon for future one-click templates).
    - Deeper feature pillars (Design/Build/Manage/Analyze/Extend), including a
      dedicated Data workspace bullet (custom post types, custom tables, form
      collection, CRM, loop sources).
    - Publisher section reframed around speed + the three-layer pipeline.
    - Roadmap heading renamed 'This is the inception' -> 'Early, on purpose';
      in-page anchors updated to match.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    DavidBabinec and claude authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    8e01106 View commit details
    Browse the repository at this point in the history
  2. feat(data): granular full-site export dialog + cmd+k Export (#68)

    * feat(data): granular full-site export dialog with per-row selection
    
    Rebuild the Data-workspace Export dialog as a sibling of the Site Import
    modal: a two-column category navigator + detail pane, everything selected
    by default so the primary action is a one-click full export. Content
    tables expose a per-row checklist (pages/posts/components/…) with per-table
    All/None; shell, media, folders and redirects are all-or-nothing toggles.
    
    Make a full export actually round-trip into a fresh instance identically by
    extending the bundle to also carry the media folder tree (+ each asset's
    folder membership) and published-URL redirects. Add includeMediaFolders /
    includeRedirects flags, an /export/summary endpoint for category counts,
    extended preview totals + import counters, and replace-strategy restore
    (parent-first folder ordering, post-write membership assignment, redirects
    inserted after their target rows). Credentials and instance-runtime state
    are deliberately excluded from the portable bundle.
    
    Replace the flat tables[]+rowIds export request with a unified
    tables: { tableId, rowIds? }[] model (omit → full export; per-entry rowIds
    → subset). A new round-trip test proves folders, membership and redirects
    survive export → import into a pristine instance.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    * feat(spotlight): make Export site reachable from cmd+k everywhere
    
    Hoist the Export dialog to a global modal (SiteExportModal) mounted at the
    admin shell and driven by adminUi.siteExport, mirroring SiteImportModal.
    Add a "Export Site" Spotlight command (data.export capability, available on
    any workspace) so export is reachable from cmd+k anywhere, and route the
    Data workspace's Export button + grid "Export selected" through the same
    openSiteExport(context) store action instead of a page-local dialog.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    * docs(site-transfer): add Export dialog screenshots
    
    Full-export overview (category navigator + detail) and the per-row content
    checklist, for the PR / feature doc.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    DavidBabinec and claude authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    cca3acd View commit details
    Browse the repository at this point in the history
  3. refactor(editor): make saved layouts the single source of truth (#66)

    Layouts now exist only as SavedLayout rows in data_rows (table_id=layouts).
    Removed the code-defined insertion presets (7 layout + 2 form templates) and
    all their wiring; the inserter's Layouts group is sourced solely from
    site.layouts. Any built-ins we ship later are seeded rows in that same table,
    indistinguishable from a user save.
    
    Also closes the save-timing hole that dropped a just-saved layout (and any
    recent unsaved edit) when leaving the editor before the 30s autosave:
    - usePersistence flushes pending changes on editor unmount (SPA navigation),
      not just on beforeunload.
    - "Save as layout" persists immediately via EDITOR_SAVE_REQUEST_EVENT rather
      than waiting for the autosave debounce.
    
    Deleted insertionPresets.ts, formPresets.ts, useInsertPreset.ts,
    useInsertFormPreset.ts, and LAYOUT_WIRES. Removed the 'layout' inserter item
    kind across the model, dialog, notch, item button, and the user-preference
    schema. Docs and tests updated.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    DavidBabinec and claude authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    a17aed3 View commit details
    Browse the repository at this point in the history
  4. fix(content): route collection create/update/delete through step-up (#72

    )
    
    Creating, updating, or deleting a content collection POSTs/PATCHes/DELETEs
    the step-up-gated data-tables endpoints (they change the public route
    surface). The Content page called the workspace mutations directly without
    useStepUp, so for accounts with step-up auth (or MFA) enabled the server's
    `401 { error: 'step_up_required' }` leaked into the dialog as raw red text
    instead of opening the password re-entry dialog.
    
    Mirror the Data page: wrap all three collection mutations in runStepUp, and
    have both collection dialogs swallow `step_up_cancelled` so backing out of
    the prompt is a silent no-op. Update/delete now bypass withEntryOp (whose
    generic catch would surface the cancellation as an error).
    
    Adds contentCollectionStepUp.test.tsx covering the create → step-up → retry
    flow and asserting the raw error code never reaches the UI.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    DavidBabinec and claude authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    34b70f1 View commit details
    Browse the repository at this point in the history
  5. feat(data): split system/custom table capabilities and lock system ta…

    …bles (#71)
    
    * feat(data): split system/custom table capabilities and lock system tables
    
    Split the single `data.tables.read`/`manage` pair into a system-vs-custom
    family so a persona (e.g. the seeded Client role) can see and manage custom
    tables without ever seeing the four internal system tables:
    
      data.custom.tables.read / data.custom.tables.manage
      data.system.tables.read / data.system.tables.manage
    
    Server enforcement (the UI mirrors it, never the sole gate):
    - GET /data/tables filters per family — system tables are returned only to
      holders of a system read cap; content-row callers (loop pickers) still see
      the full list. Single-table reads gate the same way.
    - PATCH /data/tables/:id is kind-aware (canManageTable) and runs
      assertSystemTableUpdateAllowed: a system table's identity (name, slug, route
      base, labels, kind) and built-in fields are frozen for EVERYONE, while custom
      fields and the primary field stay mutable.
    - Row writes reject built-in cell edits on the structural system tables
      (pages/components/layouts) via lockedBuiltInCellKey; posts built-ins and all
      custom fields stay editable. The editor writes those trees via its own
      endpoints, so this never blocks it.
    
    Client:
    - TableSettings renders a reduced panel for system tables (Display + Fields
      only; General/Routing/Kind/Danger hidden) with a "system-managed" note.
    - fieldGuards locks built-in fields on system tables (no edit/delete/reorder)
      while custom fields and "Add field" stay available.
    - Grid/inspector cells render built-in values read-only on structural system
      tables via isBuiltInValueLocked.
    
    Shared rules live in @core/data/systemTableGuard so server and client agree.
    Roles resynced in both dialect migrations + SYSTEM_ROLES (owner/admin get all
    four; client gets data.custom.tables.read only). Docs + tests updated; new
    unit + capability-harness integration coverage added.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    * feat(data): group sidebar by system/custom; drop system-table note
    
    - DataSidebar splits tables into "System" and "Your tables" groups (labels show
      only when both groups are present, so a custom-only persona sees a plain list).
    - Remove the explanatory banner in TableSettings for system tables — the reduced
      panel speaks for itself.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    * feat(data): move sidebar actions into section/panel headers
    
    Match the Site explorer's header pattern:
    - Export / Import become icon buttons in the "Data tables" panel header
      (`headerActions`) instead of full-width footer buttons.
    - "New table" becomes an icon button in the "Custom tables" section header.
    - Section headers (System / Custom tables) carry a count, styled like the
      Site explorer sections. The Custom section always renders (empty → "None
      yet") so the New-table action has a home; the System section renders only
      when the user can see system tables.
    - Rename "Your tables" → "Custom tables". Footer removed.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    DavidBabinec and claude authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    65bdfb8 View commit details
    Browse the repository at this point in the history
  6. Configuration menu
    Copy the full SHA
    b5846a2 View commit details
    Browse the repository at this point in the history
  7. fix(editor): make Settings modal and toolbar trailer truly global (#67)

    The Settings modal is openable from every admin route, but its General
    and Publishing sections read site data straight off the heavy editor
    store, which is only hydrated on the Site editor. On Content / Data /
    Media / Plugins / Users / Account those sections rendered a permanent
    skeleton — the modal was "global" in name only. Separately, the settings
    cog was injected per-layout: Site, Plugins/Users/Account, and Content
    had it, but Media and Data did not.
    
    Fix both:
    
    - Hoist SettingsButton into the Toolbar shell so the trailer
      (Settings → Open live page → Account) is identical on every route,
      the same way the left nav is. Remove the per-layout/per-page
      injections (AdminCanvasLayout, AdminPageLayout, ContentToolbar) and
      the now-dead ToolbarDivider.
    - Add useSiteSettingsController: one uniform site-settings source that
      delegates to the editor draft on the Site editor (joins autosave) and
      loads/saves via cmsAdapter (shell-only write + site-reload event)
      everywhere else. General and Publishing consume it instead of the
      editor store directly, so they work on every admin page without
      forcing editor-store hydration into the light layouts.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    DavidBabinec and claude authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    6988c18 View commit details
    Browse the repository at this point in the history
  8. feat(agent): add document-targeted site tools

    * feat(agent): add document-targeted site tools
    
    * fix(agent): update snapshot token benchmark read surface
    DavidBabinec authored Jun 17, 2026
    Configuration menu
    Copy the full SHA
    ab5f3e8 View commit details
    Browse the repository at this point in the history
  9. Configuration menu
    Copy the full SHA
    c001430 View commit details
    Browse the repository at this point in the history
Loading