|
|
|
|
@@ -0,0 +1,298 @@
|
|
|
|
|
---
|
|
|
|
|
phase: 16.2-plugin-provided-field-types-and-the-blog-editor-formwidget
|
|
|
|
|
plan: 02
|
|
|
|
|
subsystem: journal-admin-editor
|
|
|
|
|
tags: [sm-journal-plugin, tiptap, prosemirror, custom-elements, vite, vitest, blog-editor, embeds, uploads]
|
|
|
|
|
|
|
|
|
|
requires:
|
|
|
|
|
- phase: 16.2-01
|
|
|
|
|
provides: the field type element contract (value property, attributes, summer-change, summer-upload, summer-upload-result) and the SPA host that loads plugin field type scripts as module scripts
|
|
|
|
|
provides:
|
|
|
|
|
- custom element golem15-journal-blogeditor (BlogEditorElement), committed bundle assets/dist/blogeditor.js and blogeditor.css embedded through admin.go
|
|
|
|
|
- preservationExtensions, isPolicyUrl, RawHtml, Embed, Figure, FigureImage, Figcaption, Span, HtmlAttributes (extensions.ts)
|
|
|
|
|
- serializeDocument and captureBlocks (serializer.ts), parseEmbedUrl and EMBED_PROVIDERS (embed.ts), labelsFor (labels.ts), buildToolbar (toolbar.ts)
|
|
|
|
|
- tests/blogeditor/fixtures/editor-corpus.json, the editor's own output for the plan 03 Go policy test and the plan 04 layer copy
|
|
|
|
|
- sm-journal-plugin JS build (package.json, package-lock.json, vite.config.ts, tsconfig.json) and the scripts/check-dist.sh drift gate
|
|
|
|
|
affects: [16.2-03, 16.2-04, 16.2-05, 16.2-06, journal]
|
|
|
|
|
|
|
|
|
|
actuals:
|
|
|
|
|
tokens: 29500
|
|
|
|
|
tasks: 2
|
|
|
|
|
commits: 3
|
|
|
|
|
plan_head_before: c60abc6d6220d5f9f15659d1cc5ec7d8dd1f5020
|
|
|
|
|
plan_head_after: 086e485
|
|
|
|
|
commits_repo: ../sm-journal-plugin
|
|
|
|
|
|
|
|
|
|
tech-stack:
|
|
|
|
|
added:
|
|
|
|
|
- "@tiptap/core 3.31.4"
|
|
|
|
|
- "@tiptap/pm 3.31.4"
|
|
|
|
|
- "@tiptap/starter-kit 3.31.4"
|
|
|
|
|
- "@tiptap/extension-table 3.31.4"
|
|
|
|
|
- "@tiptap/extension-image 3.31.4"
|
|
|
|
|
- "vite 7.3.5, vitest 3.2.7, happy-dom 20.11.6, typescript 5.9.3 (dev, same pins as the admin SPA)"
|
|
|
|
|
patterns:
|
|
|
|
|
- "Untouched value: the element returns the exact served string until a user-originated docChanged transaction or a Source tab input"
|
|
|
|
|
- "Top-level block reuse: the original HTML is split by source position, each piece parsed alone, and runs of nodes still Node.eq to the captured ones are emitted as their original bytes (LCS alignment after prefix/suffix trimming)"
|
|
|
|
|
- "Stored HTML never reaches the live admin DOM: raw blocks are text cards, serializer and raw renderHTML work in an inert document, on* attributes are never captured"
|
|
|
|
|
- "Committed, minified plugin bundle with a temp-dir rebuild and diff -r drift gate"
|
|
|
|
|
|
|
|
|
|
key-files:
|
|
|
|
|
created:
|
|
|
|
|
- ../sm-journal-plugin/.gitignore
|
|
|
|
|
- ../sm-journal-plugin/package.json
|
|
|
|
|
- ../sm-journal-plugin/package-lock.json
|
|
|
|
|
- ../sm-journal-plugin/tsconfig.json
|
|
|
|
|
- ../sm-journal-plugin/vite.config.ts
|
|
|
|
|
- ../sm-journal-plugin/scripts/check-dist.sh
|
|
|
|
|
- ../sm-journal-plugin/assets/src/blogeditor/index.ts
|
|
|
|
|
- ../sm-journal-plugin/assets/src/blogeditor/element.ts
|
|
|
|
|
- ../sm-journal-plugin/assets/src/blogeditor/extensions.ts
|
|
|
|
|
- ../sm-journal-plugin/assets/src/blogeditor/serializer.ts
|
|
|
|
|
- ../sm-journal-plugin/assets/src/blogeditor/toolbar.ts
|
|
|
|
|
- ../sm-journal-plugin/assets/src/blogeditor/embed.ts
|
|
|
|
|
- ../sm-journal-plugin/assets/src/blogeditor/labels.ts
|
|
|
|
|
- ../sm-journal-plugin/assets/src/blogeditor/blogeditor.css
|
|
|
|
|
- ../sm-journal-plugin/assets/dist/blogeditor.js
|
|
|
|
|
- ../sm-journal-plugin/assets/dist/blogeditor.css
|
|
|
|
|
- ../sm-journal-plugin/tests/blogeditor/fixtures/imported-post.html
|
|
|
|
|
- ../sm-journal-plugin/tests/blogeditor/fixtures/editor-corpus.json
|
|
|
|
|
- ../sm-journal-plugin/tests/blogeditor/helpers.ts
|
|
|
|
|
- ../sm-journal-plugin/tests/blogeditor/smoke.test.ts
|
|
|
|
|
- ../sm-journal-plugin/tests/blogeditor/corpus.test.ts
|
|
|
|
|
modified:
|
|
|
|
|
- ../sm-journal-plugin/admin.go
|
|
|
|
|
- ../sm-journal-plugin/plugin_test.go
|
|
|
|
|
- ../sm-journal-plugin/README.md
|
|
|
|
|
|
|
|
|
|
key-decisions:
|
|
|
|
|
- "TipTap 3.31.4 pinned exactly for all five editor packages after the human package check (reply: packages approved)"
|
|
|
|
|
- "The bundle is built through Vite's application pipeline with a single ES entry, fixed names and preserved exports, not build.lib: library mode never minifies whitespace in ES output (587 KB vs 466 KB)"
|
|
|
|
|
- "Images are inline (Image inline: true), so an uncaptioned image is <p><img src alt></p>; a captioned one is <figure><img><figcaption></figure> through a block figureImage node"
|
|
|
|
|
- "Link parsing, rendering and commands all use isPolicyUrl: a stored link with another scheme (tel:, ftp:) keeps its text but loses the link when its block is edited, the same result the server policy gives on an edited save"
|
|
|
|
|
- "Raw HTML cards cover the plan's tags plus article, main, address, dl, iframe, video, audio, picture, object, embed, form, svg, math, script and noscript, so more stored markup survives an edit; the server still strips what its policy does not allow"
|
|
|
|
|
- "Event handler attributes (on*) are never captured by the global attribute map, so they never reach the live admin DOM"
|
|
|
|
|
- "External values (value setter, Source tab re-parse) are loaded outside the undo history"
|
|
|
|
|
|
|
|
|
|
patterns-established:
|
|
|
|
|
- "Plugin-owned JS build: package.json with exact pins, committed lockfile, committed dist, scripts/check-dist.sh"
|
|
|
|
|
- "Editor corpus: entries built only through editor commands and typing, committed as JSON, rewritten with UPDATE_CORPUS=1"
|
|
|
|
|
|
|
|
|
|
requirements-completed: [D-01, D-03, D-04, D-05, D-13, D-16, D-18]
|
|
|
|
|
|
|
|
|
|
coverage:
|
|
|
|
|
- id: D1
|
|
|
|
|
description: "Element defined once; untouched imported post returns byte for byte; no summer-change on set or echo; one summer-change per user edit; Source tab shows and edits the exact string; disabled and invalid; value kept across detach"
|
|
|
|
|
requirement: D-03
|
|
|
|
|
verification:
|
|
|
|
|
- kind: unit
|
|
|
|
|
ref: "../sm-journal-plugin/tests/blogeditor/smoke.test.ts#blog editor element (tracer)"
|
|
|
|
|
status: pass
|
|
|
|
|
human_judgment: false
|
|
|
|
|
- id: D2
|
|
|
|
|
description: "Raw HTML cards (text only, no live img/script), unmodelled attributes and spans kept on edited blocks, block reuse leaves every other top-level block byte-identical (and undo restores the exact fixture), tables with thead and bare cell text, links keep target/rel and gain none, no trailing paragraph"
|
|
|
|
|
requirement: D-04
|
|
|
|
|
verification:
|
|
|
|
|
- kind: unit
|
|
|
|
|
ref: "../sm-journal-plugin/tests/blogeditor/smoke.test.ts#preservation schema"
|
|
|
|
|
status: pass
|
|
|
|
|
human_judgment: false
|
|
|
|
|
- id: D3
|
|
|
|
|
description: "parseEmbedUrl accepts watch?v=, youtu.be/, shorts/, embed/ and refuses everything else; the embed node renders exactly the placeholder, shows a card and never an iframe; a malformed placeholder stays raw HTML; toolbar URL input"
|
|
|
|
|
requirement: D-05
|
|
|
|
|
verification:
|
|
|
|
|
- kind: unit
|
|
|
|
|
ref: "../sm-journal-plugin/tests/blogeditor/smoke.test.ts#embeds"
|
|
|
|
|
status: pass
|
|
|
|
|
human_judgment: false
|
|
|
|
|
- id: D4
|
|
|
|
|
description: "Picker and drop send summer-upload {id, file}; summer-upload-result {id, url} opens the alt/caption form and inserts img or figure; {id, error} and the 60 s timeout show the error and insert nothing; non-matching ids are ignored"
|
|
|
|
|
requirement: D-18
|
|
|
|
|
verification:
|
|
|
|
|
- kind: unit
|
|
|
|
|
ref: "../sm-journal-plugin/tests/blogeditor/smoke.test.ts#image uploads"
|
|
|
|
|
status: pass
|
|
|
|
|
human_judgment: false
|
|
|
|
|
- id: D5
|
|
|
|
|
description: "isPolicyUrl, setLink and the link input accept only http, https, mailto and relative URLs; javascript:, vbscript:, data:, tel: and tab/newline-split schemes are refused; created links have href only"
|
|
|
|
|
requirement: D-05
|
|
|
|
|
verification:
|
|
|
|
|
- kind: unit
|
|
|
|
|
ref: "../sm-journal-plugin/tests/blogeditor/smoke.test.ts#links and the URL rule"
|
|
|
|
|
status: pass
|
|
|
|
|
human_judgment: false
|
|
|
|
|
- id: D6
|
|
|
|
|
description: "Toolbar offers every D-04 feature, en and pl labels, formatting, undo/redo and table editing, raw HTML block opens Source at the insert, raw card opens its source"
|
|
|
|
|
requirement: D-13
|
|
|
|
|
verification:
|
|
|
|
|
- kind: unit
|
|
|
|
|
ref: "../sm-journal-plugin/tests/blogeditor/smoke.test.ts#toolbar"
|
|
|
|
|
status: pass
|
|
|
|
|
human_judgment: false
|
|
|
|
|
- id: D7
|
|
|
|
|
description: "editor-corpus.json (15 entries) equals a fresh build through editor commands and holds no iframe, handler, target, rel or style and only policy URLs"
|
|
|
|
|
requirement: D-05
|
|
|
|
|
verification:
|
|
|
|
|
- kind: unit
|
|
|
|
|
ref: "../sm-journal-plugin/tests/blogeditor/corpus.test.ts"
|
|
|
|
|
status: pass
|
|
|
|
|
human_judgment: false
|
|
|
|
|
- id: D8
|
|
|
|
|
description: "Committed bundle matches a fresh build, is embedded, has no eval/new Function; go vet clean"
|
|
|
|
|
requirement: D-16
|
|
|
|
|
verification:
|
|
|
|
|
- kind: other
|
|
|
|
|
ref: "bash ../sm-journal-plugin/scripts/check-dist.sh && go -C ../sm-journal-plugin vet ./..."
|
|
|
|
|
status: pass
|
|
|
|
|
human_judgment: false
|
|
|
|
|
- id: D9
|
|
|
|
|
description: "The element in the real admin SPA: typing, selection, toolbar, drag and drop, uploads through the cabana route, CSS in light and dark mode"
|
|
|
|
|
verification: []
|
|
|
|
|
human_judgment: true
|
|
|
|
|
rationale: "happy-dom covers the logic, not browser selection or layout; plan 03 registers the field type and plan 05/06 run the UAT browser check"
|
|
|
|
|
|
|
|
|
|
duration: 25min
|
|
|
|
|
completed: 2026-10-10
|
|
|
|
|
status: complete
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# Phase 16.2 Plan 02: Blog Editor element Summary
|
|
|
|
|
|
|
|
|
|
**`golem15-journal-blogeditor`, a TipTap 3.31.4 WYSIWYG editor with an exact-HTML Source tab, returns an untouched imported post byte for byte and, after an edit, keeps every unedited top-level block's original bytes; it ships raw HTML cards, figures, YouTube embed placeholders, policy-only links, SPA-mediated image uploads, a committed policy corpus and a minified bundle embedded in sm-journal-plugin with a drift gate.**
|
|
|
|
|
|
|
|
|
|
## Performance
|
|
|
|
|
|
|
|
|
|
- **Duration:** about 25 min
|
|
|
|
|
- **Started:** 2026-10-10T09:31:47Z
|
|
|
|
|
- **Completed:** 2026-10-10T09:56Z
|
|
|
|
|
- **Tasks:** 3 (Task 1 resolved by the orchestrator, Tasks 2 and 3 executed)
|
|
|
|
|
- **Files:** 24 in sm-journal-plugin (21 created, 3 modified)
|
|
|
|
|
|
|
|
|
|
## Task 1 checkpoint (resolved): package report
|
|
|
|
|
|
|
|
|
|
The orchestrator ran the plan's registry verify command for the five editor packages at 3.31.4. Result: `registry-ok`.
|
|
|
|
|
|
|
|
|
|
| Package | Version | License | Repository | Install scripts |
|
|
|
|
|
|---------|---------|---------|------------|-----------------|
|
|
|
|
|
| @tiptap/core | 3.31.4 | MIT | https://github.com/ueberdosis/tiptap | none |
|
|
|
|
|
| @tiptap/pm | 3.31.4 | MIT | https://github.com/ueberdosis/tiptap | none |
|
|
|
|
|
| @tiptap/starter-kit | 3.31.4 | MIT | https://github.com/ueberdosis/tiptap | none |
|
|
|
|
|
| @tiptap/extension-table | 3.31.4 | MIT | https://github.com/ueberdosis/tiptap | none |
|
|
|
|
|
| @tiptap/extension-image | 3.31.4 | MIT | https://github.com/ueberdosis/tiptap | none |
|
|
|
|
|
|
|
|
|
|
Maintainers are on the tiptap.dev and ueber.io domains plus tiptap-bot; 3.31.4 is the current `latest` dist-tag.
|
|
|
|
|
|
|
|
|
|
Human reply, verbatim:
|
|
|
|
|
|
|
|
|
|
> packages approved
|
|
|
|
|
|
|
|
|
|
Version Task 2 installed: **3.31.4**, pinned exactly (`npm install --save-exact`) for all five packages. Installed devDependencies: vite 7.3.5, vitest 3.2.7, happy-dom 20.11.6, typescript 5.9.3. npm 12 blocked esbuild 0.27.7's `postinstall` (not in `allowScripts`); esbuild works without it through its optional platform package `@esbuild/linux-x64`, so no script was approved.
|
|
|
|
|
|
|
|
|
|
## Accomplishments
|
|
|
|
|
|
|
|
|
|
- **Build (D-16):** private ESM `package.json` with exact pins and a committed `package-lock.json`; `vite.config.ts` builds `assets/src/blogeditor/index.ts` into `assets/dist/blogeditor.js` and `blogeditor.css` (fixed names, minified, exports kept) and configures vitest on happy-dom; `scripts/check-dist.sh` rebuilds into a temp dir and fails with `check-dist: assets/dist is stale; run npm run build and commit assets/dist`; `node_modules/` ignored; `admin.go` embeds both files.
|
|
|
|
|
- **Element (D-01, D-03, D-13):** light-DOM `BlogEditorElement` with the value/dirty logic of the interfaces skeleton, `emitUpdate: false` loads outside the undo history, `summer-change` on user edits only, an exact-string Source tab, `disabled`, `invalid`, `locale` (as `lang`) and `ui-locale` (labels); `injectCSS: false` with the ProseMirror base rules in `blogeditor.css`, every selector under the tag and only `--c-*` colours.
|
|
|
|
|
- **Preservation (D-03, D-04):** `rawHtml` text cards with an "Edit as HTML" action, `figure`/`figureImage`/`figcaption`, `embed`, `span` mark, the `htmlAttributes` global attribute map, tables without colgroup or min-width, cells without TipTap's align rewrite, StarterKit without the trailing node and with links that gain nothing.
|
|
|
|
|
- **Serializer:** `captureBlocks` splits the source by position, validates that the pieces parse to the same DOM as the whole, and parses each alone; `serializeDocument` aligns the captured blocks with the document (prefix, suffix, LCS) and emits original bytes for unchanged runs, raw HTML verbatim and the rest through `DOMSerializer` in an inert document, with tables in thead/bare-cell form. `editor.getHTML()` is never the value.
|
|
|
|
|
- **Toolbar, embeds, links, uploads (D-04, D-05, D-18):** `buildToolbar` with every listed control; `parseEmbedUrl` and `EMBED_PROVIDERS` (YouTube only); `isPolicyUrl` as Link's `isAllowedUri` and in the link input; uploads by picker or drop as `summer-upload`, insert on `summer-upload-result` with a required alt and optional caption, error and 60 s timeout in an `aria-live` status line.
|
|
|
|
|
- **Corpus (D-05):** `editor-corpus.json` with 15 entries (`embed-youtube`, `image-without-caption`, `image-with-caption`, `table-header-row`, `links`, `headings`, `bullet-list`, `ordered-list`, `blockquote`, `ordered-list-start-3`, `inline-marks`, `code-block`, `horizontal-rule`, `hard-break`, `combined`), built only through editor commands and typing.
|
|
|
|
|
- **README:** a "Blog Editor" section (element, contract, untouched values, embeds, link rule, upload flow, JS build, corpus) plus Dependencies and Testing lines.
|
|
|
|
|
|
|
|
|
|
## Task Commits
|
|
|
|
|
|
|
|
|
|
All code commits are in `../sm-journal-plugin` (branch main):
|
|
|
|
|
|
|
|
|
|
1. **Task 1: package check** - resolved by the orchestrator (no commit)
|
|
|
|
|
2. **Task 2: tracer** - `234e616` build(editor): blog editor JS build and drift gate; `47f3032` feat(editor): blog editor element with exact untouched value
|
|
|
|
|
3. **Task 3: expansion** - `086e485` feat(editor): preservation schema, block reuse, toolbar, uploads and embeds
|
|
|
|
|
|
|
|
|
|
## Bundle
|
|
|
|
|
|
|
|
|
|
- `assets/dist/blogeditor.js`: 466,051 bytes minified (147,281 bytes gzip -9)
|
|
|
|
|
- `assets/dist/blogeditor.css`: 7,480 bytes
|
|
|
|
|
- `grep -cE "eval\(|new Function\(" assets/dist/blogeditor.js` prints 0; the only `createElement("style")` is TipTap's unused `createStyleTag` (injectCSS is false).
|
|
|
|
|
|
|
|
|
|
## Verification
|
|
|
|
|
|
|
|
|
|
- Task 2 verify (tracer gate re-run before expansion): smoke tests, `check-dist.sh`, `go vet ./...` and `go test ./classes/ ./controllers/...` passed.
|
|
|
|
|
- Task 3 verify: `tsc --noEmit`, the full vitest run (2 files, 33 tests), `check-dist.sh` and `go vet ./...` pass.
|
|
|
|
|
- Acceptance greps: the tag is in the bundle, `admin.go` has one embed line for the bundle, `git check-ignore node_modules` prints `node_modules`, `serializeDocument`/`parseEmbedUrl`/`isPolicyUrl` are exported, the corpus has 15 entries with the embed, figcaption, table and `start="3"` and 0 matches of `<iframe|target=|rel=| on[a-z]+=|style=`, `data-embed` is in the bundle, and all three event names are in it.
|
|
|
|
|
|
|
|
|
|
## TDD (Task 3, tdd="true")
|
|
|
|
|
|
|
|
|
|
- **RED:** the expanded `smoke.test.ts` and `corpus.test.ts` were written and run against the Task 2 element before the schema, serializer, toolbar and element wiring. Target test `preservation schema > leaves every other top-level block byte-identical after editing one paragraph` failed on its assertion (footer, style block, table and figure flattened, link attributes reordered). `msd_run check tdd-red-evidence` returned `RED_EVIDENCE_OK` (`target_test_failed`). vitest's `tap-flat` reporter prints no `# tests/# pass/# fail` lines, so those three lines were counted from its non-skipped `ok`/`not ok` lines and appended to the record.
|
|
|
|
|
- **GREEN:** the implementation made all 33 cases pass.
|
|
|
|
|
- **Commit shape:** the plan prescribes one `feat(editor)` commit for Task 3, so no separate `test(...)` RED commit exists. `embed.ts`, `labels.ts` and `extensions.ts` were drafted just before the tests, so the `parseEmbedUrl` and `isPolicyUrl` cases were already green at RED time; every behavior that needed the schema, serializer, toolbar or upload flow was red.
|
|
|
|
|
|
|
|
|
|
## Decisions Made
|
|
|
|
|
|
|
|
|
|
See key-decisions in the frontmatter.
|
|
|
|
|
|
|
|
|
|
## Deviations from Plan
|
|
|
|
|
|
|
|
|
|
### Auto-fixed Issues
|
|
|
|
|
|
|
|
|
|
**1. [Rule 3 - Blocking] Fixture source not readable; synthetic fixture with the same structure**
|
|
|
|
|
- **Found during:** Task 2
|
|
|
|
|
- **Issue:** The environment's permission classifier refused every read of `/media/nvme/dev/golem15/grzybyfunkcjonalne.pl/journal-export/ekstrakt-z-turkey-tail.json` (PII data handling), including `ls`.
|
|
|
|
|
- **Fix:** `imported-post.html` is a synthetic post with the structure the plan requires: rendered-markdown blocks (h2, h3, p with em/strong, ul, ol, blockquote, links), the author footer in the shape the repository already uses in `classes/format_html_test.go` (`itemscope`, `itemtype`, `itemprop`, `div` and `span` with `style`, neutral name and `cdn.example.test` photo), a `style` block, a table with thead, a figure with img and figcaption, a link with `target` and `rel`, a paragraph with `"` entities, plus `&`, ` `, `–` and `×`. No real author or post text is copied. A human may swap in the real export later; the tests do not depend on its wording.
|
|
|
|
|
- **Commit:** 47f3032
|
|
|
|
|
|
|
|
|
|
**2. [Rule 3 - Blocking] Library mode does not minify ES output**
|
|
|
|
|
- **Found during:** Task 2
|
|
|
|
|
- **Issue:** Vite 7 `build.lib` with `formats: ['es']` forces `minifyWhitespace: false` (587 KB); the acceptance assumes a minified bundle.
|
|
|
|
|
- **Fix:** the same single ES entry through `rollupOptions.input` with `preserveEntrySignatures: 'exports-only'`, `entryFileNames: 'blogeditor.js'`, `assetFileNames: 'blogeditor[extname]'`, `modulePreload: false` (384 KB at Task 2, 466 KB at Task 3). The admin loads it as `type="module"`, as before.
|
|
|
|
|
- **Files modified:** vite.config.ts
|
|
|
|
|
- **Commit:** 234e616
|
|
|
|
|
|
|
|
|
|
**3. [Rule 2 - Security] on* attributes never captured; uploaded URLs checked**
|
|
|
|
|
- **Found during:** Task 3
|
|
|
|
|
- **Issue:** a global attribute map that captures every unmodelled attribute would render `onerror`/`onload` from stored HTML into the live admin DOM; an upload answer could carry a URL outside the policy.
|
|
|
|
|
- **Fix:** `captureAttributes` and `renderCaptured` skip `on*` names (test asserts the editor's `img` has no `on*` attribute); a `summer-upload-result` URL that fails `isPolicyUrl` is shown as an upload error. The serializer and the raw HTML `renderHTML` build DOM only in an inert document.
|
|
|
|
|
- **Commit:** 086e485
|
|
|
|
|
|
|
|
|
|
**4. [Rule 1 - Bug] Replaced values in the undo history; edited value lost on detach**
|
|
|
|
|
- **Found during:** Task 3
|
|
|
|
|
- **Issue:** TipTap's `setContent` records history, so undo after an external value brought back the replaced document; after `disconnectedCallback` destroyed the editor, a dirty value fell back to the served string.
|
|
|
|
|
- **Fix:** loads run as `chain().setMeta('addToHistory', false).setContent(...)`; on disconnect a dirty WYSIWYG value is kept as the element's value. Both have tests.
|
|
|
|
|
- **Commit:** 086e485
|
|
|
|
|
|
|
|
|
|
**5. Files outside the plan's lists**
|
|
|
|
|
- `serializer.ts` was created in Task 2 (minimal, `DOMSerializer` only) so the tracer never used `getHTML()`; Task 3 replaced it as planned.
|
|
|
|
|
- `tests/blogeditor/helpers.ts` holds the shared mount/typing helpers of both test files.
|
|
|
|
|
- `plugin_test.go` (`TestJournalSVGEmbed`) asserts that `AdminFS` holds both bundle files and that the script defines the tag. The root package's `TestMain` starts testcontainers, so this runs only on a Docker host (plan 06); it compiles and `go vet` is clean here.
|
|
|
|
|
|
|
|
|
|
**6. Scope notes on plan wording**
|
|
|
|
|
- `RAW_HTML_TAGS` covers more tags than the plan lists (see key-decisions).
|
|
|
|
|
- Stored links with a scheme outside the policy keep their text but not the link once their block is edited (the plan said "preserved in the editor"); unedited blocks keep their bytes. This matches what the server does on an edited save.
|
|
|
|
|
- In edited blocks, link attributes come out as `target`, `rel`, `href` (TipTap merges the null option defaults first); values are unchanged and unedited blocks keep their bytes.
|
|
|
|
|
- Table cells drop TipTap's `align` attribute so a stored `style="text-align: ..."` or `align` is kept verbatim through the attribute map instead of being rewritten.
|
|
|
|
|
- In the corpus, the `blockquote` builder sits between the two ordered-list builders so the `combined` document keeps two lists (TipTap joins adjacent lists).
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
**Total deviations:** 4 auto-fixed (2 blocking, 1 security, 1 bug) plus file and scope notes. **Impact:** no change to the element contract, the embed markup or the corpus shape that plans 03 and 04 consume.
|
|
|
|
|
|
|
|
|
|
## Issues Encountered
|
|
|
|
|
|
|
|
|
|
- No Docker on this host: the root package's Go tests (including the new AdminFS assertion) need testcontainers and run in plan 06.
|
|
|
|
|
- happy-dom covers the element logic, but not browser selection, IME, real drag and drop or layout; the browser check belongs to the UAT step.
|
|
|
|
|
|
|
|
|
|
## Known Stubs
|
|
|
|
|
|
|
|
|
|
None. The `field-name`, `record-id` and `options` attributes are accepted and unused, because this type has no options and does not need the record id; that is the contract, not a stub.
|
|
|
|
|
|
|
|
|
|
## Next Phase Readiness
|
|
|
|
|
|
|
|
|
|
- Plan 03 registers `blogeditor`/`mlblogeditor` with `Element: "golem15-journal-blogeditor"`, `Scripts: ["assets/dist/blogeditor.js"]` and `Styles: ["assets/dist/blogeditor.css"]` (already in `adminFS`), and reads `tests/blogeditor/fixtures/editor-corpus.json` in place for the Go policy test. The policy must allow `figure` `data-embed`/`data-id`, any `img` `alt` (the corpus has one with `:`, `?` and `"`), and `ol` `start`.
|
|
|
|
|
- Plan 04 copies `editor-corpus.json` byte for byte into the layer and mirrors the embed provider table (one YouTube entry, id `^[A-Za-z0-9_-]{11}$`).
|
|
|
|
|
|
|
|
|
|
## Broken-windows ledger
|
|
|
|
|
|
|
|
|
|
- deviation: `tests/blogeditor/fixtures/imported-post.html` (sm-journal-plugin) is synthetic.
|
|
|
|
|
- unrun-verify: the new `TestJournalSVGEmbed` assertion in sm-journal-plugin `plugin_test.go` needs Docker (plan 06).
|
|
|
|
|
|
|
|
|
|
## Self-Check: PASSED
|
|
|
|
|
|
|
|
|
|
- Created files exist in ../sm-journal-plugin: assets/src/blogeditor/{index,element,extensions,serializer,toolbar,embed,labels}.ts, blogeditor.css, assets/dist/blogeditor.js and .css, tests/blogeditor/{helpers,smoke.test,corpus.test}.ts, fixtures/imported-post.html and editor-corpus.json, scripts/check-dist.sh, package.json, package-lock.json, vite.config.ts, tsconfig.json, .gitignore.
|
|
|
|
|
- Commits exist in ../sm-journal-plugin: 234e616, 47f3032, 086e485.
|