From 2e15cd50ae42a3d579b793a27b4f89fa9988bb29 Mon Sep 17 00:00:00 2001 From: sakarpaisen Date: Thu, 22 Oct 2026 11:49:02 +0300 Subject: [PATCH] docs(web): fix relative links broken by the monorepo move The deploy config, lockfile and crash-recovery store now live at the repository root or under src/store/persistence. Links from apps/web and apps/web/docs pointed at the old locations, so they now traverse to the root or use the moved path. Anchors and destinations are otherwise unchanged. --- apps/web/PRODUCT.md | 16 ++++++++-------- apps/web/README.md | 2 +- apps/web/docs/README.md | 2 +- apps/web/docs/auth.md | 2 +- apps/web/docs/local-development.md | 2 +- apps/web/docs/v1-to-current-changelog.md | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/web/PRODUCT.md b/apps/web/PRODUCT.md index b20e086..f1180b3 100644 --- a/apps/web/PRODUCT.md +++ b/apps/web/PRODUCT.md @@ -30,7 +30,7 @@ Grounded in: name and home page in [src/constants.ts](src/constants.ts), dual pa | What the code shows | Evidence | |---|---| | Documents are stored only in the browser, per browser profile: IndexedDB `perssonify-docs`, plus `perssonify-snapshots` for version history. | [src/store/persistence/idb-storage-adapter.ts](src/store/persistence/idb-storage-adapter.ts), [src/store/history/snapshot-store.ts](src/store/history/snapshot-store.ts) | -| There is one server function, and it exists only for Import from URL, which uses it only with the user's consent. | [api/import.js](api/import.js), [vercel.json](vercel.json), [src/store/importExport/url-import.ts](src/store/importExport/url-import.ts) | +| There is one server function, and it exists only for Import from URL, which uses it only with the user's consent. | [api/import.js](api/import.js), [vercel.json](../../vercel.json), [src/store/importExport/url-import.ts](src/store/importExport/url-import.ts) | | Three distributions ship from one codebase. | Standalone app: [index.html](index.html) → [src/main.tsx](src/main.tsx) → [src/App.tsx](src/App.tsx). Iframe embed: [embed.html](embed.html) → [src/embed.tsx](src/embed.tsx). npm package `@perssonify/editor`: [src/index.tsx](src/index.tsx) plus the `exports` map in [package.json](package.json). | | The app is installable, with a manifest and a service worker. | [vite.app.config.ts](vite.app.config.ts) | @@ -38,7 +38,7 @@ Grounded in: name and home page in [src/constants.ts](src/constants.ts), dual pa - **Release history.** Version 1.77.0, with 30 release entries in [CHANGELOG.md](CHANGELOG.md) running from 1.4.x. - **Deployment.** - - [vercel.json](vercel.json) is a Vercel deploy config, and `APP_URL = https://markdownstudio.com` in [src/constants.ts](src/constants.ts). + - [vercel.json](../../vercel.json) is a Vercel deploy config, and `APP_URL = https://markdownstudio.com` in [src/constants.ts](src/constants.ts). - A QA script drives the live site ([scripts/live-preview-walkthrough.mjs](scripts/live-preview-walkthrough.mjs)). - Whether that site runs this exact build cannot be verified from code. - **Test investment.** @@ -56,7 +56,7 @@ Grounded in: name and home page in [src/constants.ts](src/constants.ts), dual pa - The ad slots are placeholders, and only development builds render them ([src/ui/shared/adUnit/index.tsx](src/ui/shared/adUnit/index.tsx), [src/utils/build-gates.ts](src/utils/build-gates.ts)). - There is no analytics or telemetry, so no product usage data is collected. - There are no accounts or sync. - - The iframe embed is built by `bun run build:embed` but not deployed ([vercel.json](vercel.json) builds only the app). + - The iframe embed is built by `bun run build:embed` but not deployed ([vercel.json](../../vercel.json) builds only the app). ## 3. Users and roles @@ -217,7 +217,7 @@ The URL-import relay isn't a user role, but it has its own gates. The app uses i | Copy as Markdown / HTML / JSON | Copies the whole active document to the clipboard. | [src/PerssonifyEditor.tsx:1953-1957](src/PerssonifyEditor.tsx#L1953-L1957) | Done | | Copy as AI prompt | Copies an LLM-ready prompt: the active document plus the documents it links to and from (one hop, up to 20), with metadata. It reads the last-*saved* copies, runs paragraphs together, and lists relationships by internal id. | [src/integration/prompt-builder.ts](src/integration/prompt-builder.ts), [src/PerssonifyEditor.tsx:1999-2015](src/PerssonifyEditor.tsx#L1999-L2015) | Partial | | Copy graph JSON | Copies the link graph (nodes and edges) to the clipboard, from the Tools menu or the map header. | [src/hooks/useLinkIndex.ts](src/hooks/useLinkIndex.ts), [src/store/links/link-index.ts](src/store/links/link-index.ts) | Done | -| Share / embed link builder | Tools ▸ Share / embed link, and inline on the Help page. It builds two things:
• a standalone link with view mode, read-only and import URL (plus ads, in development builds only) — this works;
• an iframe snippet that also carries theme, accent, chrome and background — this points at `/embed.html`, which the production build doesn't contain.
Links carry settings, never document content. | [src/ui/share/linkBuilder/index.tsx:173-199](src/ui/share/linkBuilder/index.tsx#L173-L199), [vercel.json](vercel.json) | Partial | +| Share / embed link builder | Tools ▸ Share / embed link, and inline on the Help page. It builds two things:
• a standalone link with view mode, read-only and import URL (plus ads, in development builds only) — this works;
• an iframe snippet that also carries theme, accent, chrome and background — this points at `/embed.html`, which the production build doesn't contain.
Links carry settings, never document content. | [src/ui/share/linkBuilder/index.tsx:173-199](src/ui/share/linkBuilder/index.tsx#L173-L199), [vercel.json](../../vercel.json) | Partial | ### 4.8 Links, backlinks, map and outline @@ -287,7 +287,7 @@ The URL-import relay isn't a user role, but it has its own gates. The app uses i | Custom storage adapter | Sends document storage through a host-supplied adapter. The default database is still opened at startup, and snapshots and the crash stash still use the browser's own storage, but they follow the host's `storageConfig` namespace, or can be switched off there. `clearLocalData()` erases them, e.g. on logout. | [src/store/documents/documentStore.ts](src/store/documents/documentStore.ts), [src/store/persistence/storage-adapter.ts](src/store/persistence/storage-adapter.ts) | Partial | | Associations | Host-supplied extra edges between documents. Held in memory, and merged only into `getGraph` and `copyAsPrompt`; never shown in the map or backlinks. | [src/integration/associations.ts](src/integration/associations.ts) | Partial | | `setFieldSchemas`; `documents`, `fieldSchemas`, `linkResolver`, `onFieldChange`, `onExport` props | Deprecated (`@deprecated` in the types, and one console warning per page load in development builds when used), still accepted and ignored: `setFieldSchemas` writes a global nobody reads; the props are never read or fired. `onLinkClick` replaces `linkResolver`. | [src/integration/deprecation.ts](src/integration/deprecation.ts), [src/integration/PerssonifyEditorIntegration.tsx](src/integration/PerssonifyEditorIntegration.tsx) | Dead | -| Iframe embed | `embed.html` with URL parameters: `mode`, `theme`, `accent`, `accentColor`, `chrome`, `bg` (including `transparent`), `allowed_origin`, `readonly`. Built by `bun run build:embed` but **not part of the deployed site**: `/embed.html` on the production domain serves the standalone app. Self-hosters must serve the `dist-embed` build. | [src/embed.tsx](src/embed.tsx), [vite.embed.config.ts](vite.embed.config.ts), [vite.app.config.ts](vite.app.config.ts), [vercel.json](vercel.json) | Partial | +| Iframe embed | `embed.html` with URL parameters: `mode`, `theme`, `accent`, `accentColor`, `chrome`, `bg` (including `transparent`), `allowed_origin`, `readonly`. Built by `bun run build:embed` but **not part of the deployed site**: `/embed.html` on the production domain serves the standalone app. Self-hosters must serve the `dist-embed` build. | [src/embed.tsx](src/embed.tsx), [vite.embed.config.ts](vite.embed.config.ts), [vite.app.config.ts](vite.app.config.ts), [vercel.json](../../vercel.json) | Partial | | postMessage bridge | About 20 actions (document reads and writes, theme, mode, chrome, export, prompt, associations) with request/response, plus events (ready, change, save, link click). Unknown actions are rejected. Talks only to the origins resolved as §3 describes, and to nobody when none qualifies. | [src/integration/host-bridge.ts](src/integration/host-bridge.ts), [src/integration/embed-origins.ts](src/integration/embed-origins.ts) | Done | | Client helper | `createEditorClient(iframe)` wraps the bridge for host pages. | [src/integration/host-bridge.ts](src/integration/host-bridge.ts) | Done | @@ -297,7 +297,7 @@ The URL-import relay isn't a user role, but it has its own gates. The app uses i |---|---|---|---| | Installable app | PWA manifest (standalone display, icons) and a service worker. Installing is optional for offline use; on Safari it also protects the library from the 7-day storage limit. | [vite.app.config.ts](vite.app.config.ts) | Done | | Offline use | Works after one online visit, installed or not. The service worker precaches every file the app can load, on-demand features included, in the background after the first load, and serves its own copy of the page when the network is gone. Import from URL and images hosted elsewhere still need a connection. A real-browser suite uses every on-demand feature for the first time offline. | [vite.app.config.ts](vite.app.config.ts), [public/sw-recover.js](public/sw-recover.js), [e2e/cases/pwa.mjs](e2e/cases/pwa.mjs) | Done | -| Update delivery | Prompted, never forced: "A new version of Markdown Studio is ready" with a Reload button, and an open tab checks for a new version hourly. A tab reloads by itself only when it is online, has nothing unsaved and is in the background. The boot watchdog recovers a load that cannot start only on evidence of a failed app script, and only online. A missing on-demand file 404s (it is no longer answered with the page), and the app reloads for it only when online with nothing unsaved. | [src/pwa/sw-updates.ts](src/pwa/sw-updates.ts), [src/main.tsx](src/main.tsx), [index.html](index.html), [public/sw-recover.js](public/sw-recover.js), [vercel.json](vercel.json) | Done | +| Update delivery | Prompted, never forced: "A new version of Markdown Studio is ready" with a Reload button, and an open tab checks for a new version hourly. A tab reloads by itself only when it is online, has nothing unsaved and is in the background. The boot watchdog recovers a load that cannot start only on evidence of a failed app script, and only online. A missing on-demand file 404s (it is no longer answered with the page), and the app reloads for it only when online with nothing unsaved. | [src/pwa/sw-updates.ts](src/pwa/sw-updates.ts), [src/main.tsx](src/main.tsx), [index.html](index.html), [public/sw-recover.js](public/sw-recover.js), [vercel.json](../../vercel.json) | Done | | Crash screen | An "Editor Crashed" card with Recover Editor (remounts the editor), Export my documents (a full backup ZIP built straight from storage) and Copy diagnostics. A file that failed to download gets its own screen with Reload. | [src/ui/shared/errorBoundary/index.tsx](src/ui/shared/errorBoundary/index.tsx), [src/utils/emergency-backup.ts](src/utils/emergency-backup.ts) | Done | | Blocked browser storage | The app doesn't crash when the browser blocks storage. If IndexedDB can't open, the editor still opens and editing works, but nothing is saved or restored; startup reports "Failed to initialize", and a persistent "Not saving" banner stays up for as long as that is true. | [src/utils/safe-storage.ts](src/utils/safe-storage.ts), [src/store/persistence/storage-adapter.ts](src/store/persistence/storage-adapter.ts), [src/ui/notices/durabilityNotices/index.tsx](src/ui/notices/durabilityNotices/index.tsx), [e2e/cases/faults.mjs](e2e/cases/faults.mjs) | Done | @@ -365,7 +365,7 @@ The production site does not serve the embed, so the Share / embed builder's sni | Service | Used for | Where | Notes | |---|---|---|---| -| Vercel | Hosts the static app and runs the one serverless function | [vercel.json](vercel.json), [api/import.js](api/import.js) | Every non-`/api` path is rewritten to the app. The service worker and `index.html` are sent with no-cache headers. | +| Vercel | Hosts the static app and runs the one serverless function | [vercel.json](../../vercel.json), [api/import.js](api/import.js) | Every non-`/api` path is rewritten to the app. The service worker and `index.html` are sent with no-cache headers. | | Import relay (first-party, on Vercel) | Fetches a URL for Import from URL when the host blocks direct browser reads, and only if the user has agreed | [api/import.js](api/import.js), [src/store/importExport/url-import.ts](src/store/importExport/url-import.ts) | See the relay rules below the table. | | GitHub, Dropbox, Google Drive / Docs / Sheets, any web host | Sources for Import from URL | [src/store/importExport/import-url-normalize.ts](src/store/importExport/import-url-normalize.ts), [src/store/importExport/url-import.ts](src/store/importExport/url-import.ts) | GitHub file links become `raw.githubusercontent.com` links; Dropbox gets `dl=1`; Drive file links become direct downloads. Fetched from the browser without cookies or referrer. Google Docs is always rejected (§7.2). | | Remote image hosts | Images that documents reference by URL | [src/extensions/SizedImage.ts](src/extensions/SizedImage.ts), [src/store/importExport/print-export.ts](src/store/importExport/print-export.ts), [src/utils/remote-images.ts](src/utils/remote-images.ts) | The browser loads them directly and sends no referrer (`index.html`, `embed.html` and `vercel.json` all set `no-referrer`). Settings ▸ Privacy ▸ Block remote images stops these requests from the next load, through a CSP `img-src` the app shell installs; the npm library never installs it. Together with URL import, this is the only traffic to third parties. | @@ -444,7 +444,7 @@ Found by reading the code, ordered by how directly users hit them. - `themeVars` is not sanitised. - `storageAdapter` doesn't fully replace browser storage (§4.12). - Only one editor per page is supported: the stores are module singletons. This is documented; remounting the editor works. -12. **The embed isn't deployed**, so the builder's iframe snippet is broken in production. The snippet is built at [src/ui/share/linkBuilder/index.tsx:197](src/ui/share/linkBuilder/index.tsx#L197); [vite.app.config.ts:99](vite.app.config.ts#L99) and [vercel.json](vercel.json) show the embed isn't part of the deployed build. +12. **The embed isn't deployed**, so the builder's iframe snippet is broken in production. The snippet is built at [src/ui/share/linkBuilder/index.tsx:197](src/ui/share/linkBuilder/index.tsx#L197); [vite.app.config.ts:99](vite.app.config.ts#L99) and [vercel.json](../../vercel.json) show the embed isn't part of the deployed build. ### 7.3 Docs and in-app text that promise more than the code does diff --git a/apps/web/README.md b/apps/web/README.md index 18a9288..d501ac8 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -757,7 +757,7 @@ relay for "Import from URL" — **the only server-side code in the product**. Fu guide, threat model, limitations and hardening roadmap: **[docs/import-relay.md](docs/import-relay.md)**. -**Configuration** ([vercel.json](vercel.json)): +**Configuration** ([vercel.json](../../vercel.json)): - Install command: `bun install --frozen-lockfile` - Build command: `bun run build:app && node scripts/strip-sourcemaps.mjs` — the build writes the third-party licence notice from its hidden sourcemaps, then the maps are moved out of diff --git a/apps/web/docs/README.md b/apps/web/docs/README.md index d6dc599..0efa0f1 100644 --- a/apps/web/docs/README.md +++ b/apps/web/docs/README.md @@ -17,7 +17,7 @@ Markdown Studio is a **browser-based** markdown editor that runs two editing sur ## Quickstart -This repo uses **[Bun](https://bun.sh)** as its package manager and runner (see [`bun.lock`](../bun.lock) and [`vercel.json`](../vercel.json)). `npm` also works for installing, but the deploy pipeline and lockfile are Bun. +This repo uses **[Bun](https://bun.sh)** as its package manager and runner (see [`bun.lock`](../../../bun.lock) and [`vercel.json`](../../../vercel.json)). `npm` also works for installing, but the deploy pipeline and lockfile are Bun. ```bash git clone https://github.com/Perssonify/Markdown-Studio.git diff --git a/apps/web/docs/auth.md b/apps/web/docs/auth.md index 9cffd29..fd5f815 100644 --- a/apps/web/docs/auth.md +++ b/apps/web/docs/auth.md @@ -33,7 +33,7 @@ The removal is recorded in git: 84d7099 refactor: remove login form and authentication logic from App component ``` -[`vercel.json`](../vercel.json) today is a static SPA config — install with `bun install --frozen-lockfile`, build with `build:app` and then strip the sourcemaps, output `dist-app/`, rewrite everything **except `/api/`, `/assets/` and `/.well-known/`** to `/index.html`, and send security headers (a report-only CSP, enforced anti-framing, `nosniff`, `no-referrer` — see [deployment.md](deployment.md#content-security-policy)). No middleware, and the single serverless function (`api/import.js`) is the import relay, not an auth gate. +[`vercel.json`](../../../vercel.json) today is a static SPA config — install with `bun install --frozen-lockfile`, build with `build:app` and then strip the sourcemaps, output `dist-app/`, rewrite everything **except `/api/`, `/assets/` and `/.well-known/`** to `/index.html`, and send security headers (a report-only CSP, enforced anti-framing, `nosniff`, `no-referrer` — see [deployment.md](deployment.md#content-security-policy)). No middleware, and the single serverless function (`api/import.js`) is the import relay, not an auth gate. > If a future requirement reintroduces auth, do **not** restore the old middleware blindly — design it against the current static-SPA + optional-embed model. Note this doc and [gotchas.md](gotchas.md) when you do. diff --git a/apps/web/docs/local-development.md b/apps/web/docs/local-development.md index 9f9f79b..385a114 100644 --- a/apps/web/docs/local-development.md +++ b/apps/web/docs/local-development.md @@ -6,7 +6,7 @@ | Tool | Version | Why | |------|---------|-----| -| [Bun](https://bun.sh) | 1.4.0 | Canonical package manager + runner, pinned by `packageManager` in [`package.json`](../package.json). [`bun.lock`](../bun.lock) is the committed lockfile; the verify gate and [`vercel.json`](../vercel.json) install with `bun install --frozen-lockfile`, so a dependency change that does not update `bun.lock` fails the build. | +| [Bun](https://bun.sh) | 1.4.0 | Canonical package manager + runner, pinned by `packageManager` in [`package.json`](../package.json). [`bun.lock`](../../../bun.lock) is the committed lockfile; the verify gate and [`vercel.json`](../../../vercel.json) install with `bun install --frozen-lockfile`, so a dependency change that does not update `bun.lock` fails the build. | | Node.js | `^22.22.2`, `^24.15.0` or `>=26` — 24 is the one to use ([`.node-version`](../../../.node-version)) | The `engines` range in `package.json`: the Nodes EVERY direct dependency accepts (the narrowest are jsdom 30's `^22.22.2 \|\| ^24.15.0 \|\| >=26` and Vitest 5's, which skips 23 and 25; `ops-node-range.test.js` recomputes it from `node_modules`). The build scripts (`scripts/*.mjs`, `build/*.mjs`, `e2e/`) and the import relay run on it. | | A browser at or above the floor | Safari/iOS 16.4, Chrome/Edge 111, Firefox 128 (A097) | The app relies on IndexedDB + DOMParser + (optionally) the RegExp `d`/`hasIndices` flag for find-and-replace capture coloring. The e2e campaign drives Chromium from `playwright-core` (`bunx playwright-core install chromium`), and the cross-engine suites Firefox and WebKit too (`install firefox webkit`, then `E2E_BROWSER=firefox` or `webkit`). | diff --git a/apps/web/docs/v1-to-current-changelog.md b/apps/web/docs/v1-to-current-changelog.md index c31d365..ce5958a 100644 --- a/apps/web/docs/v1-to-current-changelog.md +++ b/apps/web/docs/v1-to-current-changelog.md @@ -357,7 +357,7 @@ The detailed sections above stop at v1.60.0. This is what changed between then a it, and a formatting chord dispatched mid-utterance. - **1.74:** `beforeunload` alone never covered unload — IndexedDB cannot complete a write while the page is torn down. A synchronous localStorage stash - ([`src/store/crash-recovery.ts`](../src/store/crash-recovery.ts)) is written on both + ([`src/store/crash-recovery.ts`](../src/store/persistence/crash-recovery.ts)) is written on both `beforeunload` and `pagehide` (iOS Safari fires only the latter) and adopted on the next `initialize`, one-shot and bounded. - **1.74:** a dropped file navigated the tab away, taking the current autosave window