From d5d9c51fe6b89b57f44145c2efc8d38564df586d Mon Sep 17 00:00:00 2001 From: Hubertoink Date: Wed, 30 Sep 2026 11:06:42 +0200 Subject: [PATCH 1/5] feat(cms): preserve Hochstaett deployment baseline --- docs/deployment/hochstaett-fork.md | 64 +++++ docs/editor.md | 13 + docs/features/agent.md | 4 + docs/features/content-workspace.md | 17 ++ docs/features/data-workspace.md | 6 + docs/features/html-import.md | 4 +- docs/features/loops.md | 34 ++- docs/features/templates.md | 1 + docs/features/visual-components.md | 8 + server/ai/tools/content/readTools.test.ts | 27 +++ server/ai/tools/content/readTools.ts | 14 +- server/ai/tools/content/systemPrompt.ts | 10 +- server/ai/tools/content/writeTools.ts | 2 +- server/ai/tools/site/readTools.test.ts | 44 ++++ server/ai/tools/site/readTools.ts | 11 +- server/ai/tools/site/systemPrompt.ts | 1 + server/db/migrations-pg.ts | 86 +++++++ server/db/migrations-sqlite.ts | 83 +++++++ server/handlers/cms/data/preview.ts | 3 +- server/handlers/cms/data/tables.ts | 6 + server/handlers/cms/loop.ts | 13 +- server/publish/branchPreview.ts | 4 +- server/publish/imageLightboxRuntime.ts | 3 + server/publish/loopPrefetch.ts | 49 +++- server/publish/publicRenderer.ts | 4 +- server/publish/relationLoopPrefetch.ts | 47 ++++ server/publish/renderTreeWalk.ts | 12 +- server/publish/runtime/previewRuntime.ts | 2 +- server/publish/scrollAnimationRuntime.ts | 3 + .../__tests__/post-publication-date.test.ts | 129 ++++++++++ server/repositories/data/publish.ts | 11 +- server/router.ts | 22 ++ .../canvas/canvasDetailsInteraction.test.tsx | 31 +++ .../canvas/canvasDetailsPreview.test.ts | 71 ++++++ .../canvas/templatePreviewBindings.test.tsx | 30 ++- src/__tests__/core/sanitize.test.ts | 13 + src/__tests__/data/markdown.test.ts | 33 +++ .../editor/readOnlyNodeTree.test.tsx | 50 ++++ src/__tests__/htmlImport/mapping.test.ts | 19 ++ src/__tests__/loops/relationLoops.test.ts | 228 ++++++++++++++++++ .../AnimationControl.test.tsx | 31 +++ .../publisher/animationPresets.test.ts | 62 +++++ .../publisher/previewOverlay.test.tsx | 14 ++ .../publisher/scrollAnimationRuntime.test.ts | 82 +++++++ .../server/cmsTemplateRoutes.test.ts | 12 + src/__tests__/server/loopPrefetch.test.ts | 4 +- src/__tests__/templates/dateTokens.test.ts | 31 +++ src/__tests__/utils/urlValidation.test.ts | 12 +- .../BodyFloatingMenu/BodyFloatingMenu.tsx | 24 +- .../BodyFloatingMenu/InsertLinkDialog.tsx | 44 ++++ .../components/LiveCanvas/LiveCanvas.tsx | 1 + .../MediaNodeToolbar.module.css | 19 ++ .../MediaNodeToolbar/MediaNodeToolbar.tsx | 80 ++++-- src/admin/pages/content/nodes/MediaNode.ts | 44 +++- .../DataGrid/cells/CellEditorRenderer.tsx | 2 +- .../DataGrid/cells/RelationCell.tsx | 34 +-- .../DataGrid/cells/cells.module.css | 7 + .../RelationPickerDialog.module.css | 46 ++++ .../RelationPickerDialog.tsx | 39 ++- .../site/canvas/ComponentPreviewSource.tsx | 48 ++++ .../pages/site/canvas/IframeFrameSurface.tsx | 4 + .../VisualComponentModeControl.module.css | 16 +- .../canvas/VisualComponentModeControl.tsx | 2 + .../site/canvas/useCanvasDetailsPreview.ts | 59 +++++ .../site/canvas/useCanvasScrollAnimations.ts | 12 + .../site/canvas/useEntryRelationItems.ts | 54 +++++ .../pages/site/canvas/useLoopPreviewItems.ts | 5 + .../site/hooks/useTemplatePreviewContext.ts | 8 +- .../PropertiesPanel/ClassPropertyRow.tsx | 6 +- .../PropertiesPanel/LoopPropertiesView.tsx | 24 +- .../PropertiesPanel/usePropertiesPanelData.ts | 25 +- .../pages/site/preview/PreviewOverlay.tsx | 4 +- .../property-controls/AnimationControl.tsx | 59 +++++ .../property-controls/MediaLibraryControl.tsx | 9 +- .../store/slices/previewSelectionSlice.ts | 29 +++ src/admin/pages/site/store/slices/uiSlice.ts | 21 -- src/admin/pages/site/store/store.ts | 5 +- src/core/animationPresets.ts | 31 +++ src/core/htmlImport/rules.ts | 10 +- src/core/imageLightboxRuntime.ts | 119 +++++++++ src/core/loops/entryScope.ts | 41 ++++ src/core/loops/index.ts | 2 + src/core/loops/sources/dataRows.ts | 46 +++- src/core/loops/sources/dataRowsCells.ts | 58 +++++ src/core/loops/sources/dataRowsMedia.ts | 38 --- src/core/loops/sources/entryField.ts | 24 +- src/core/markdown/markdownDocument.ts | 23 +- src/core/markdown/mediaImagePresentation.ts | 88 +++++++ src/core/markdown/renderMarkdown.ts | 18 +- src/core/persistence/cmsData.ts | 2 + src/core/publisher/classCss.ts | 5 +- src/core/publisher/render.ts | 20 +- src/core/publisher/renderConfig.ts | 4 +- src/core/publisher/renderLoop.ts | 5 + src/core/publisher/reset.ts | 8 + src/core/sanitize.ts | 29 ++- src/core/scrollAnimationRuntime.ts | 91 +++++++ src/core/templates/dateToken.ts | 21 ++ src/core/templates/tokenInterpolation.ts | 5 +- src/core/utils/urlValidation.ts | 13 +- src/modules/base/outlet/OutletEditor.tsx | 4 +- src/modules/base/utils/ReadOnlyNodeTree.tsx | 28 ++- 102 files changed, 2686 insertions(+), 237 deletions(-) create mode 100644 docs/deployment/hochstaett-fork.md create mode 100644 server/publish/imageLightboxRuntime.ts create mode 100644 server/publish/relationLoopPrefetch.ts create mode 100644 server/publish/scrollAnimationRuntime.ts create mode 100644 server/repositories/data/__tests__/post-publication-date.test.ts create mode 100644 src/__tests__/canvas/canvasDetailsInteraction.test.tsx create mode 100644 src/__tests__/canvas/canvasDetailsPreview.test.ts create mode 100644 src/__tests__/loops/relationLoops.test.ts create mode 100644 src/__tests__/property-controls/AnimationControl.test.tsx create mode 100644 src/__tests__/publisher/animationPresets.test.ts create mode 100644 src/__tests__/publisher/scrollAnimationRuntime.test.ts create mode 100644 src/__tests__/templates/dateTokens.test.ts create mode 100644 src/admin/pages/content/components/BodyFloatingMenu/InsertLinkDialog.tsx create mode 100644 src/admin/pages/site/canvas/ComponentPreviewSource.tsx create mode 100644 src/admin/pages/site/canvas/useCanvasDetailsPreview.ts create mode 100644 src/admin/pages/site/canvas/useCanvasScrollAnimations.ts create mode 100644 src/admin/pages/site/canvas/useEntryRelationItems.ts create mode 100644 src/admin/pages/site/property-controls/AnimationControl.tsx create mode 100644 src/admin/pages/site/store/slices/previewSelectionSlice.ts create mode 100644 src/core/animationPresets.ts create mode 100644 src/core/imageLightboxRuntime.ts create mode 100644 src/core/loops/entryScope.ts create mode 100644 src/core/loops/sources/dataRowsCells.ts create mode 100644 src/core/markdown/mediaImagePresentation.ts create mode 100644 src/core/scrollAnimationRuntime.ts create mode 100644 src/core/templates/dateToken.ts diff --git a/docs/deployment/hochstaett-fork.md b/docs/deployment/hochstaett-fork.md new file mode 100644 index 000000000..ef091c047 --- /dev/null +++ b/docs/deployment/hochstaett-fork.md @@ -0,0 +1,64 @@ +# Hochstaett deployment fork + +This fork preserves the locally tested CMS customizations used by the Hochstaett site. + +- Fork: https://github.com/Hubertoink/Instatic +- Upstream: https://github.com/CoreBunch/Instatic +- Initial integration branch: `feat/hochstaett-deployment-baseline` +- Upstream base: `f92e8dc1` (0.0.20) + +## Source and runtime data + +Git contains application code, tests and documentation. Website content lives in +the CMS database and uploads volume. Database files, uploaded media, backups, +environment files and secret keys are not deployment source and must be +transferred separately through a private backup/restore process. + +The integration branch includes relation/repeater loops, preview improvements, +publication-date handling, animation presets, and content image sizing, +lightbox and link insertion. Review these local changes through the fork's draft +pull request before merging; the integration branch is not a production release. + +## Git remotes + +`origin` points to this fork; `upstream` points to CoreBunch. Feature branches +and pull requests belong to the fork unless a contribution to CoreBunch is +explicitly intended. + +```sh +git clone https://github.com/Hubertoink/Instatic.git +cd Instatic +git remote add upstream https://github.com/CoreBunch/Instatic.git +git switch --track origin/feat/hochstaett-deployment-baseline +``` + +Fetch upstream updates and integrate them on a separate review branch. Do not +reset the fork to upstream when preserving these customizations. + +## Container handoff + +Build the image from the reviewed fork commit: + +```sh +docker build -t instatic-hochstaett:staging . +``` + +The upstream release workflow currently targets `ghcr.io/corebunch/instatic`. +It has not been adapted for this fork: do not create release tags as a way to +publish fork images. A separate fork image workflow and registry configuration +are the next deployment step. + +Mittwald needs an image available in a registry, persistent storage, HTTPS +routing and the application environment described in [docker-image.md](docker-image.md). +For a copy of the existing SQLite installation, preserve the data and uploads +volumes and its secret encryption key using [backup-restore.md](backup-restore.md). + +Publishing inside a local CMS instance does not deploy that instance to Mittwald. +An online staging instance publishes to its own staging site. + +## Validation at initial handoff + +The production Docker build (TypeScript and Vite), ESLint, targeted content tests, +and desktop/mobile lightbox checks passed locally. The full Windows test run +was not green; unrelated architecture, platform and UI failures remain for +review. Consult the draft pull request for the handoff results. diff --git a/docs/editor.md b/docs/editor.md index 8ed3fdb9b..176bb45da 100644 --- a/docs/editor.md +++ b/docs/editor.md @@ -8,6 +8,9 @@ The frontend is a single React 19 + Vite SPA mounted at `/admin`. Inside it, two ## TL;DR +- **Entrance animations:** select a class, then **Styles → Effects → Animation** to choose Fade in, Slide up/down/from left/from right, or Zoom in. Duration, delay (milliseconds), and easing are editable. Choose Trigger: On load or On scroll into view. Scroll effects run once when the element intersects the viewport; the configured delay starts at that point. Design mode keeps scroll effects visible for editing; Live mode and published pages use the same IntersectionObserver runtime, including entries inserted by loops. Without JavaScript/Web Animations support, or with reduced motion, scroll content remains visible. None disables an inherited animation; Inherited clears the local value. Custom CSS preserves arbitrary imported animation shorthands. Preset keyframes are emitted by the shared canvas/publisher class-CSS generator only when used. Reduced-motion preferences keep content visible without entrance movement, including during delays. +- **Local links:** the URL control accepts `/page#section`, `#section`, `./page`, `../page`, query references, and `tel:` as well as HTTP(S)/mailto links. Protocol-relative URLs and unsafe schemes remain rejected. + - **Entry:** `src/admin/main.tsx` mounts `` with React 19 root-level error callbacks. `flushSync` forces the initial render synchronous to cut LCP. - **Router:** `src/admin/lib/routing/` — in-house router replacing `react-router-dom`. Ten workspace/page routes are wrapped in a per-route `` and ``, with root redirects plus a final `path="/admin/*"` catch-all redirecting unknown admin URLs to `/admin/dashboard` (login form when unauthenticated) instead of rendering an empty tree. Public-site 404s are NOT claimed — the publish pipeline's NotFound handling owns those. - **Cold path:** entry chunk is tiny. `AuthenticatedAdmin` is `React.lazy` and only loads post-login. Each workspace page is wrapped in `prewarmedLazy(...)`: the active page fires its import at module evaluation; the remaining pages pre-warm via `requestIdleCallback` after first paint so subsequent nav is synchronous (no Suspense flicker). @@ -337,6 +340,7 @@ The store is composed of **12 slices**, each created by a factory in `store/slic | `selectionSlice` | `selectedNodeId`, `hoveredNodeId` | | `canvasSlice` | Zoom, pan, `activeBreakpointId`, `activeConditionId`, `canvasMode` ('select'|'pan'|'insert'), `canvasView` ('design'|'live'), `runScripts` | | `uiSlice` | Site editor panel visibility, unsaved-changes flag, insert picker, `componentizeEditorRequest` | +| `previewSelectionSlice` | Session-only template and component preview source selections | | `classSlice` | Style-rule CRUD, node ↔ class assignment, ambient selector creation | | `filesSlice` | `SiteFile` CRUD | | `visualComponentsSlice`| Visual Component CRUD | @@ -390,6 +394,14 @@ Selectors are pure reads. Mutations go through actions (`useEditorStore.getState ### 1. Design mode and live mode +Native `details`/`summary` accordions can be opened and closed by clicking their +summary in either view, or with Enter/Space while the summary is focused. +Selection still works normally. Selecting a layer inside a collapsed body +reveals its ancestor accordions automatically. This state belongs only to the +rendered editor frame: it does not change the page tree, collaboration document, +undo history, or the published initial `open` attribute. Reloading the editor +restores the authored state. Read-only composed regions are not toggled. + `CanvasRoot` switches between two rendering surfaces based on `canvasView`: - **Design mode** (`canvasView === 'design'`): `CanvasRoot` → `CanvasTransformLayer` → `BreakpointFrame` → `IframeFrameSurface` → `NodeRenderer`. Each breakpoint gets its own iframe rendered side-by-side inside the pan/zoom transform layer. The author sees all breakpoints at once and can zoom in/out. The canvas opens at 50% (`INITIAL_ZOOM`) so several frames fit in view; reset (Cmd/Ctrl+0, the toolbar % button) goes to 100% (`RESET_ZOOM`). @@ -788,3 +800,4 @@ See [docs/features/plugin-system.md](features/plugin-system.md) for the plugin S - `src/__tests__/architecture/canvas-aware-selectors.test.ts` - `src/__tests__/architecture/spotlight-no-direct-store-mutation.test.ts` - `src/__tests__/architecture/keybindings-registry-single-source.test.ts` + diff --git a/docs/features/agent.md b/docs/features/agent.md index cc3c90be2..347d10b6c 100644 --- a/docs/features/agent.md +++ b/docs/features/agent.md @@ -2,6 +2,10 @@ The AI Agent is a model-powered assistant integrated into the Site editor and Content workspace. The shared Agent Panel owns conversation state, provider selection, streaming, history, and the browser bridge; each workspace supplies its own snapshot builder and tool executor. +Relation and repeater catalogs are exposed through the existing tools. `site_list_loop_sources` preserves relation `targetTableSlug` and `allowMultiple`, media metadata, and repeater item fields with their binding tokens. An `entry.field` loop uses `...` inside an entry context. Relation children bind the target table's fields; repeater children bind item fields. The HTML importer preserves the selected field and defaults contextual loops to authored order. + +`content_get_collection_schema` includes relation target table IDs/cardinality and repeater item schemas (including select option IDs). Content writes use bare row ID strings or `null` for single relations and string arrays for multi-relations; media uses the same shape with asset IDs. Repeaters use ordered `{ id, cells }` items. The Content bridge edits post-type entries; schema management and a Data-workspace toolset are not provided. + In the Site editor, the agent reads the current page snapshot, plans a sequence of edits, and executes them by calling tools. Structure is written as semantic HTML (`site_insert_html` / `site_replace_node_html`); styling is written as CSS — a `