一键导入
edit-bloom-book
Use when an agent needs to edit or repair a Bloom book HTML file while preserving the strict Bloom.html schema used by Bloom Desktop.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when an agent needs to edit or repair a Bloom book HTML file while preserving the strict Bloom.html schema used by Bloom Desktop.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Run, launch, screenshot, or drive the Bloom desktop app (Bloom.exe with embedded WebView2). Use when you need to start Bloom from this worktree, attach to a running Bloom, switch workspace tabs, take a screenshot, inspect DOM/console/network over CDP, or stop a Bloom instance.
Use when an agent needs to determine if Bloom is already running, detect whether the running Bloom came from a different worktree, kill Bloom or dotnet-watch parents, start Bloom from the current worktree, attach to the embedded WebView2 over CDP, inspect DOM/console/network, use dev-browser to inspect or run e2e tests against the actual exe instead of CURRENTPAGE.
build and run automated Playwright end-to-end tests for Canvas Tool behavior on CURRENTPAGE.
Add or review localizable strings in Bloom XLF files
React useEffect best practices from official docs. Use when writing/reviewing useEffect, useState for derived values, data fetching, or state synchronization. Teaches when NOT to use Effect and better alternatives.
Audit and fix WCAG color contrast in Bloom game themes. Generates a scrollable HTML preview showing every element of every theme in its real colors with low-contrast pairs flagged, then drives an AI-curated, human-approved fix loop. Use when adding or editing a game theme in gamesThemes.less, when a game element (dashed target, draggable, button, checkbox) is hard to see, or when a tester wants to eyeball all themes at once.
| name | edit-bloom-book |
| description | Use when an agent needs to edit or repair a Bloom book HTML file while preserving the strict Bloom.html schema used by Bloom Desktop. |
| argument-hint | Bloom.html path and requested change or repair |
| user-invocable | true |
Edit a Bloom.html book without breaking Bloom's structural expectations. Preserve the DOM schema Bloom relies on for page identity, multilingual text, images, canvas elements, xMatter regeneration, and split-pane layouts.
Bloom.html directly..bloom-page, .bloom-translationGroup, .bloom-editable, .bloom-canvas, or split-pane containers.node .github/skills/edit-bloom-book/validateBloomBook.mjs path/to/Book.htm
node .github/skills/edit-bloom-book/validateBloomBook.mjs book1.htm book2.htm
.bloom-page elements#bloomDataDivmeta[name="BloomFormatVersion"].bloom-page.bloom-page at body root levellang on every .bloom-editable.bloom-translationGroup / div.bloom-editable child structurebloom-canvas structuresp, img, and textareaThis is the concise model the validator enforces. * means zero or more, ? means optional, and quoted class names mean the element must include that class, though it may also carry other Bloom classes.
Book ::= html(head(meta[name=BloomFormatVersion], ...), body(DataDiv, Page+))
DataDiv ::= div#bloomDataDiv
Page ::= div."bloom-page"[id] as direct child of body
containing MarginBox
PageChild ::= PageLabel | PageDescription | MarginBox | PageCoverColor | TranslationGroup
MarginBox ::= div."marginBox" containing PageContent*
TranslationGroup ::= div."bloom-translationGroup" not nested in another TranslationGroup
containing (Editable | textarea[lang])+
Editable ::= div."bloom-editable"[lang] as direct child of TranslationGroup
SplitPane ::= div."split-pane".("horizontal-percent" | "vertical-percent")
(SplitComponent, SplitDivider, SplitComponent, SplitResizeShim?)
HorizontalSplit ::= SplitPane with exactly one component."position-top",
one component."position-bottom", and divider."horizontal-divider"
VerticalSplit ::= SplitPane with exactly one component."position-left",
one component."position-right", and divider."vertical-divider"
RootSplitHolder ::= div."marginBox"."split-pane-component" containing SplitPane
SplitComponent ::= div."split-pane-component" containing exactly one direct SplitInner | SplitPane
SplitInner ::= div."split-pane-component-inner"
SplitResizeShim ::= div."split-pane-resize-shim"
Canvas ::= div."bloom-canvas" inside Page containing LegacyImage+ | CanvasElement+
LegacyImage ::= img as a direct child of Canvas
CanvasElement ::= div."bloom-canvas-element" as direct child of Canvas
containing ImageContainer | TranslationGroup | VideoContainer
ImageContainer ::= div."bloom-imageContainer" as direct child of CanvasElement
containing exactly one direct img
Important compatibility branches:
bloom-canvas > img directly, without .bloom-canvas-element or .bloom-imageContainer..split-pane-resize-shim..split-pane directly inside a .split-pane-component instead of using .split-pane-component-inner at that level; leaf split components still use .split-pane-component-inner.contenteditable="true" is common on user-editable text, but not a validity invariant: some generated/xMatter fields have .bloom-editable and content classes while intentionally being non-editable.body contains the book-level metadata div and page divs..bloom-page must be a direct child of body..bloom-page inside another element.id values must remain unique.id values on img, p, and textarea must also remain unique.Canonical top-level shape:
<html>
<head>...</head>
<body>
<div id="bloomDataDiv">...</div>
<div class="bloom-page ..." id="...">...</div>
<div class="bloom-page ..." id="...">...</div>
</body>
</html>
#bloomDataDiv near the start of body.#bloomDataDiv stores book-wide values using elements like <div data-book="bookTitle" lang="en">...</div>.data-book values in page content and values in #bloomDataDiv are linked and should stay in sync.data-xmatter-page and any paired attributes stored in #bloomDataDiv.<div> with class .bloom-page and a stable id..pageLabel, .pageDescription, .marginBox, and sometimes other layout wrappers.lang="" on the page itself is common and should not be normalized away unless the surrounding pattern clearly requires something else.Typical page shape:
<div class="bloom-page numberedPage customPage A5Portrait side-right" id="..." data-page="..." lang="">
<div class="pageLabel" lang="en" data-i18n="...">...</div>
<div class="pageDescription" lang="en"></div>
<div class="marginBox">
...page content...
</div>
</div>
.marginBox is the usual primary page content wrapper and should normally be preserved..marginBox inside another wrapper; do not flatten or "clean up" structure just because it looks redundant..split-pane, .split-pane-component, .split-pane-component-inner, .split-pane-divider, and .split-pane-resize-shim in a strict pattern.Canonical split-pane patterns:
<div class="split-pane horizontal-percent">
<div class="split-pane-component position-top" style="bottom: 50%;">
<div class="split-pane-component-inner">...</div>
</div>
<div class="split-pane-divider horizontal-divider" style="bottom: 50%;"></div>
<div class="split-pane-component position-bottom" style="height: 50%;">
<div class="split-pane-component-inner">...</div>
</div>
<div class="split-pane-resize-shim"></div>
</div>
<div class="split-pane vertical-percent">
<div class="split-pane-component position-left">
<div class="split-pane-component-inner" style="position: relative;">...</div>
</div>
<div class="split-pane-divider vertical-divider"></div>
<div class="split-pane-component position-right">
<div class="split-pane-component-inner" style="position: relative;">...</div>
</div>
<div class="split-pane-resize-shim"></div>
</div>
.bloom-translationGroup wrapper..bloom-editable children, each with a lang attribute.data-default-languages exactly unless the task explicitly requires language-visibility changes.V, N1, N2, L1, L2, L3, auto, and *..bloom-editable from one translation group to another unless the semantic field is clearly the same.Canonical translation group:
<div class="bloom-translationGroup" data-default-languages="V,N1">
<div class="bloom-editable bloom-content1 bloom-visibility-code-on" lang="en" contenteditable="true">
<p>Text</p>
</div>
<div class="bloom-editable bloom-contentNational1 bloom-visibility-code-on" lang="fr" contenteditable="true">
<p>Texte</p>
</div>
</div>
.bloom-editable marks user-editable content. Preserve it..bloom-editable should keep its lang.contenteditable="true" on user-editable text unless the surrounding pattern clearly uses a non-editable stored value..bloom-content1, .bloom-content2, .bloom-content3, .bloom-contentNational1, .bloom-contentNational2, and .Equation-style.lang="z" is a special prototype-only placeholder used in templates and some generated content. Do not rewrite it to a real language unless the task is explicitly converting template content into concrete book content.data-book, data-audiorecordingmode, spellcheck, tabindex, role, and aria-label when present..bloom-canvas containing one or more .bloom-canvas-element children..bloom-canvas-element.bloom-backgroundImage > .bloom-imageContainer > img.data-book, data-copyright, data-license, data-creator, data-imgsizebasedon, and comic data-bubble payloads.svg.comical-generated or .bloom-canvas-element nodes on comic pages.Canonical canvas background-image pattern:
<div class="bloom-canvas bloom-has-canvas-element" data-tool-id="canvas">
<div class="bloom-canvas-element bloom-backgroundImage" style="width:100%; height:100%;">
<div class="bloom-imageContainer" data-tool-id="canvas">
<img data-book="coverImage" src="placeHolder.png" />
</div>
</div>
</div>
img as data-copyright, data-creator (the photographer/artist), and data-license (a license token such as cc-by, cc-by-sa, or cc0; the license version is not encoded here).ImageUpdater.UpdateImgMetadataAttributesToMatchImage(), which reads metadata from the file and rewrites (or, when the file has none, removes) these data-* attributes. So setting only the HTML attributes does NOT stick: a photo with no embedded metadata shows the red "?©" badge and its data-* get blanked on the next save.SIL.Windows.Forms.ClearShare.Metadata → set CopyrightNotice / Creator / License (e.g. CreativeCommonsLicense.FromLicenseUrl(...)) → WriteIntellectualPropertyOnly(path). Bloom reads it back via RobustFileIO.MetadataFromFile (TagLib#). After that, the HTML data-* are derived/repopulated automatically. See the bird-book skill for a working batch tool (embed-metadata/).data-xmatter-page and related persisted attributes..bloom-page away from body root level.id unless you are intentionally creating a new page and generating a new unique one.id values..bloom-translationGroup wrappers around multilingual content.lang from .bloom-editable elements..bloom-content1 or .bloom-contentNational1 without understanding the field's visibility behavior.data-default-languages unless the task explicitly changes language visibility.data-* will persist; Bloom rewrites those from the image file's embedded metadata. Embed it in the file, and edit only while Bloom is closed..bloom-editable..bloom-editable inside the existing .bloom-translationGroup; do not create a parallel structure unless the page pattern already does that..bloom-canvas / .bloom-canvas-element / .bloom-imageContainer structure.id to a .bloom-page..bloom-page back to body root level..bloom-translationGroup wrapper when multiple language variants clearly belong to the same field.lang and contenteditable="true" on .bloom-editable nodes when they were accidentally stripped..bloom-content1 or .bloom-contentNational1 when the field's role is obvious from neighboring sibling editables..bloom-imageContainer inside an existing .bloom-canvas-element when the intended image pattern is unambiguous..split-pane-component-inner wrappers without changing the overall split geometry.data-default-languages..marginBox or split-pane boundaries.data-bubble JSON or canvas sizing metadata.data-book, check whether the matching #bloomDataDiv value also needs to be repaired..marginBox and .split-pane-component-inner are meaningful even when they look decorative.lang="z", treat that as intentional template placeholder content.Simple text field:
<div class="bloom-translationGroup" data-default-languages="auto">
<div class="bloom-editable bloom-content1 bloom-visibility-code-on" lang="en" contenteditable="true">
<p>Text</p>
</div>
</div>
Image-only page content:
<div class="marginBox">
<div class="split-pane-component-inner">
<div class="bloom-canvas">
<img src="placeHolder.png" alt="" />
</div>
</div>
</div>
Canvas page content:
<div class="split-pane-component-inner">
<div class="bloom-canvas bloom-has-canvas-element" data-tool-id="canvas">
<div class="bloom-canvas-element bloom-backgroundImage" style="width:100%; height:100%;">
<div class="bloom-imageContainer" data-tool-id="canvas">
<img src="placeHolder.png" />
</div>
</div>
</div>
</div>
src/BloomExe/Book/HtmlDom.cs: ValidateBook() enforces page presence, root-level .bloom-page placement, and unique ids.src/BloomExe/Book/TranslationGroupManager.cs: defines how Bloom prepares .bloom-translationGroup and .bloom-editable structures for active languages.src/BloomExe/Book/BookData.cs: defines how #bloomDataDiv, data-book, and data-xmatter-page metadata are synchronized.src/content/templates/template books/standard-page-mixins.pug: canonical split-pane and canvas page patterns.src/content/templates/xMatter/TemplateStarter-XMatter/TemplateStarter-XMatter.pug: canonical xMatter cover/about structures.src/BloomBrowserUI/utils/shared.ts: editor attachment depends on content classes plus contenteditable="true".src/BloomTests/Book/BookStorageTests.cs: concrete examples of valid and repaired page structures, including nested .marginBox and canvas cases.Bloom.html and identify the specific page or field to change.