diff --git a/.github/workflows/fork-image.yml b/.github/workflows/fork-image.yml new file mode 100644 index 000000000..ff47daa80 --- /dev/null +++ b/.github/workflows/fork-image.yml @@ -0,0 +1,43 @@ +name: Fork staging image + +on: + workflow_dispatch: + push: + branches: [main, chore/mittwald-staging] + +permissions: + contents: read + packages: write + +concurrency: + group: fork-image-${{ github.ref }} + cancel-in-progress: false + +jobs: + image: + if: github.repository == 'Hubertoink/Instatic' + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and publish fork image + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: true + tags: ghcr.io/hubertoink/instatic:staging-${{ github.sha }} + labels: | + org.opencontainers.image.source=https://github.com/Hubertoink/Instatic + org.opencontainers.image.url=https://github.com/Hubertoink/Instatic + build-args: | + INSTATIC_VERSION=staging-${{ github.sha }} + INSTATIC_REVISION=${{ github.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/docs/deployment/hochstaett-fork.md b/docs/deployment/hochstaett-fork.md new file mode 100644 index 000000000..b418f53da --- /dev/null +++ b/docs/deployment/hochstaett-fork.md @@ -0,0 +1,70 @@ +# 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 fork image workflow, `.github/workflows/fork-image.yml`, builds Linux amd64 +images on pushes to `main` and `chore/mittwald-staging`, and supports manual +dispatch after merging. Images are tagged +`ghcr.io/hubertoink/instatic:staging-`. The Docker build runs +TypeScript checking and the production frontend build. It does not run tests +or lint; those remain separate verification steps. + +The upstream release workflow still targets `ghcr.io/corebunch/instatic`; +do not create release tags to publish fork images. The fork workflow only +publishes images and does not change a running Mittwald container. + +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 `