From 78e18415dd4a2a3ec245893723af1053c3fd6205 Mon Sep 17 00:00:00 2001 From: kev1n77 Date: Mon, 3 Aug 2026 16:20:53 +0800 Subject: [PATCH] feat(skills): bundle BitFun skin authoring skill --- .../create-bitfun-skin/SKILL.md | 114 + .../cinematic-animated-wallpaper/SKILL.md | 134 + .../references/default-palette.json | 21 + .../references/palette-contract.md | 37 + .../references/style-playbook.md | 145 + .../references/surface-plan.json | 1154 + .../scripts/build_assets.py | 163 + .../scripts/build_cinematic_skin.py | 483 + .../scripts/cinematic_recipe.py | 473 + .../scripts/scaffold_cinematic.py | 89 + .../references/appearance-registry.json | 17959 ++++++++++++++++ .../references/authoring-workflow.md | 52 + .../references/media-quality-policy.md | 43 + .../references/package-contract.md | 130 + .../references/renderer-contracts.md | 54 + .../references/style-example-contract.md | 75 + .../scripts/bitfun_appearance.py | 1786 ++ .../scripts/build_support.py | 211 + .../scripts/media_support.py | 526 + .../scripts/sync_registry.py | 148 + .../scripts/test_bitfun_appearance.py | 117 + .../scripts/test_cinematic_tools.py | 224 + .../create-bitfun-skin/scripts/verify_host.py | 186 + .../tools/implementations/skills/builtin.rs | 15 + .../tools/implementations/skills/catalog.rs | 1 + .../agent-runtime/src/skills/catalog.rs | 4 + .../agent-runtime/tests/skill_contracts.rs | 5 + 27 files changed, 24349 insertions(+) create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/SKILL.md create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/SKILL.md create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/default-palette.json create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/palette-contract.md create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/style-playbook.md create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/surface-plan.json create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/build_assets.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/build_cinematic_skin.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/cinematic_recipe.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/scaffold_cinematic.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/appearance-registry.json create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/authoring-workflow.md create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/media-quality-policy.md create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/package-contract.md create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/renderer-contracts.md create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/style-example-contract.md create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/bitfun_appearance.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/build_support.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/media_support.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/sync_registry.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/test_bitfun_appearance.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/test_cinematic_tools.py create mode 100644 src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/verify_host.py diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/SKILL.md b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/SKILL.md new file mode 100644 index 0000000000..47a23ce1c4 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/SKILL.md @@ -0,0 +1,114 @@ +--- +name: create-bitfun-skin +description: Inspect the current BitFun Appearance contract and author, repair, package, or validate sparse .bitfun-appearance skins. Use when Codex needs to discover registered scenes/components/parts/states/facets, check Style IR properties or renderer tokens, initialize or inspect a package, diagnose import failures, verify a skin against a BitFun checkout, or choose a bundled style example for design-specific guidance. +--- + +# BitFun Appearance Authoring + +Use this Skill as the authority for what a BitFun skin may change and how that change is represented. Let the user's assets, product direction, and the selected example determine the visual design. + +The bundled registry and package references define capabilities. Files under `examples/` define optional style choices; they never expand the contract or prescribe coverage for another skin. + +## Route the task + +- For a new or repaired package, read [authoring-workflow.md](references/authoring-workflow.md) and [package-contract.md](references/package-contract.md). +- For renderer configuration, also read [renderer-contracts.md](references/renderer-contracts.md). +- For generated video or WebP assets, also read [media-quality-policy.md](references/media-quality-policy.md). +- For a style-specific design, load only the closest example after reading the parent workflow. +- For import failures, inspect the issue path, query that surface, and repair the sparse manifest instead of copying another package. +- For BitFun contract changes, synchronize the registry before changing examples or skins. + +## Discover the exposed surface + +Run commands from this Skill directory: + +```powershell +python scripts/bitfun_appearance.py contract list components +python scripts/bitfun_appearance.py contract list scenes +python scripts/bitfun_appearance.py contract list renderers +python scripts/bitfun_appearance.py contract show components +python scripts/bitfun_appearance.py contract show scenes +python scripts/bitfun_appearance.py contract properties +python scripts/bitfun_appearance.py contract tokens css +python scripts/bitfun_appearance.py contract tokens widget +``` + +Only author registered surfaces, Parts, facets, states, renderer adapters, tokens, and Style IR properties returned by these commands. A Part descriptor may further restrict its allowed properties. + +A component owns only the Parts returned for that component. Nested visual owners expose their own component ids and must be styled through those ids. Do not infer Parts from DOM structure, another skin, or an example surface plan. + +Surface-level states may use an `ancestorPart` selector rooted at `root`. Define the state rule using the registered state id; do not reproduce host selectors in the package. + +Important independent owners include `toolbar-mode`, `floating-mini-chat`, `session-menu`, `composer-voice-input`, `miniapp-bubble-welcome`, `session-title-config`, `assistant-card`, `workspace-item`, `external-mcp-overview`, `miniapp-customize-panel`, `user-message-edit-composer`, `voice-input-diagnostics`, `flow-chat-turn-rail`, and `copyable-text-preview`. Query each owner before styling it. + +The following Parts are not registered and must not be used: + +- `toolbar-mode.input` +- `toolbar-mode.sessionMenu` +- `floating-mini-chat.input` +- `floating-mini-chat.inputBar` +- `floating-mini-chat.sessionMenu` + +## Author a sparse package + +Initialize the smallest valid overlay: + +```powershell +python scripts/bitfun_appearance.py init --id --name "" --mode +``` + +Edit `/appearance.json` and declare only the values the skin needs to change. BitFun merges the package over its built-in light or dark Appearance. + +Packages may contain PNG, JPEG, WebP, and GIF images. MP4 or WebM is allowed only through top-level `backgroundMedia`, and every video requires an image poster. CSS files, selectors, markup, scripts, fonts, remote URLs, SVG, and executable content are unsupported. + +## Select a style example + +Currently bundled: + +| Example | Load when | Do not inherit blindly | +| --- | --- | --- | +| [cinematic-animated-wallpaper](examples/cinematic-animated-wallpaper/SKILL.md) | Animated character artwork, source-derived glass materials, image-led cards, or illustrated dialogs | Its asset roles, crop defaults, palette, and 43-component/7-scene surface selection | + +Read [style-example-contract.md](references/style-example-contract.md) before adding or restructuring an example. An example is a design recipe validated against the registry, not a second contract snapshot. + +## Validate importability + +```powershell +python scripts/bitfun_appearance.py validate +python scripts/bitfun_appearance.py build --output .bitfun-appearance +python scripts/bitfun_appearance.py validate .bitfun-appearance +python scripts/bitfun_appearance.py inspect +``` + +When a BitFun checkout is available, verify with the production registry, Validator, Compiler, and packaged-video display-dimension probe: + +```powershell +python scripts/verify_host.py .bitfun-appearance --report --strict-warnings +``` + +Standalone validation proves package and bundled-registry compatibility. Host verification proves the selected checkout accepts and compiles the package. Neither proves runtime visual quality; import the skin and inspect the actual surfaces. + +## Reuse deterministic support + +Example authors may import: + +- `scripts/build_support.py` for JSON, hashing, package initialization, validation, archive creation, host verification, build-record checks, and runtime checklist creation. +- `scripts/media_support.py` for video probing, contact sheets, robust frame extraction, normalized crops, WebP output, VP9 encoding, preview sheets, and host video limits. + +Keep style-specific asset roles, crop decisions, palettes, material construction, renderer choices, surface selection, and runtime checks inside the example. + +## Maintain the registry + +Treat the bundled registry as the offline authority. Confirm it before targeting a checkout: + +```powershell +python scripts/sync_registry.py --check +``` + +Refresh it only from a clean checkout after BitFun changes Appearance descriptors: + +```powershell +python scripts/sync_registry.py +``` + +Do not hand-edit `references/appearance-registry.json`. Use `--allow-dirty` only for local investigation, never for a distributed snapshot. diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/SKILL.md b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/SKILL.md new file mode 100644 index 0000000000..97e0289649 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/SKILL.md @@ -0,0 +1,134 @@ +--- +name: create-bitfun-cinematic-wallpaper-skin +description: Apply the cinematic animated-wallpaper style recipe to a BitFun Appearance skin. Use after loading the parent create-bitfun-skin Skill when the user provides animated character artwork and wants a host-managed video background, source-derived glass materials, image-led cards, illustrated dialogs, reproducible builds, or cinematic drift checks. +--- + +# Cinematic animated-wallpaper recipe + +Load the parent `create-bitfun-skin` Skill first. The parent registry and package references define what can be changed; this example defines one optional visual strategy. + +Do not reuse this example's 43-component/7-scene coverage, asset roles, crop defaults, palette, opacity, or material choices unless they fit the user's artwork and intent. Its [surface-plan.json](references/surface-plan.json) is an example style selection validated against one registry revision, not a list of all current Appearance surfaces. + +Read [style-playbook.md](references/style-playbook.md) for visual decisions and [palette-contract.md](references/palette-contract.md) before creating a custom palette. +Read the parent [media-quality-policy.md](../../references/media-quality-policy.md) before overriding video or WebP quality. + +## Style strategy + +This recipe uses: + +- a host-managed VP9 WebM background with an image poster; +- source-derived artwork for floating surfaces, sidebars, cards, and dialogs; +- translucent cinematic materials over the artwork; +- a semantic palette shared by Style IR and renderer adapters; +- broad surface coverage suitable for an immersive character-led skin. + +Keep the built-in Appearance for any owner that does not benefit from this treatment. Query the parent registry before adding, removing, or changing a selected surface. + +## Output layout + +Keep the importable project isolated from source and report files: + +```text +/ + package/ + appearance.json + assets/ + background.webm + floating.webp + sidebar.webp + card-detail.webp + card-portrait.webp + dialog.webp + preview.webp + sources/ + palette.json + surface-plan.json + contact-sheet.png + asset-preview-sheet.png + .bitfun-appearance + host-verification.json + skin-build.json + runtime-checklist.json +``` + +Validate and build only `/package`. Reports and source files inside that directory invalidate the project. + +## Choose a source frame + +Generate only the timestamped contact sheet: + +```powershell +python examples/cinematic-animated-wallpaper/scripts/build_cinematic_skin.py contact ` + --source ` + --output +``` + +Inspect `contact-sheet.png`, select a frame time, and decide normalized crops. Do not continue with blind crops. + +## Create a palette + +Copy [default-palette.json](references/default-palette.json) outside the Skill and tune it to the source artwork. Keep every required semantic key and six-digit hex value. + +The recipe maps the palette into cinematic materials plus CSS, Widget, Monaco, xterm, Mermaid, and Canvas renderer settings. The parent Skill does not prescribe these mappings for other styles. + +## Build the recipe + +Run from the parent Skill directory: + +```powershell +python examples/cinematic-animated-wallpaper/scripts/build_cinematic_skin.py build ` + --source ` + --output ` + --id ` + --name "" ` + --frame-time ` + --palette ` + --bitfun-repo +``` + +The thin recipe entrypoint combines cinematic asset generation and manifest construction with the parent's generic build support. It initializes the package, generates previews and assets, copies resolved recipe inputs, scaffolds the manifest, validates project and archive, runs strict host verification, and writes the reproducibility record. + +Production host warnings fail by default. Use `--allow-warnings` only for diagnosis. + +The default `--video-quality auto` tries codec-lossless VP9 only for small workloads, otherwise starts at CRF 20 and adapts only when the encoded file exceeds the host limit. The default `--static-quality-mode auto` tries lossless WebP before a quality fallback. Use `--video-quality lossless` only when exceeding 64 MiB should be treated as a hard failure; use `--video-crf` to reproduce a known fixed encoding. + +Inspect `asset-preview-sheet.png`. Re-run `build --force` with adjusted crop arguments when any role is poorly framed. + +## Rebuild and check + +```powershell +python examples/cinematic-animated-wallpaper/scripts/build_cinematic_skin.py rebuild ` + --output + +python examples/cinematic-animated-wallpaper/scripts/build_cinematic_skin.py check ` + --output +``` + +Build records use `bitfun.appearance.recipe-build` and identify this recipe. + +`skin-build.json` records the requested policy, every attempted encoding, and the selected codec-lossless/CRF or WebP quality result. `check` verifies source, palette, surface-plan, manifest and archive drift, package validity, and current host compatibility. Add `--skip-host-verify` only for an offline check. + +## Recipe implementation + +- [cinematic_recipe.py](scripts/cinematic_recipe.py) owns palette interpretation, materials, renderers, asset declarations, and manifest generation. +- [build_assets.py](scripts/build_assets.py) owns cinematic asset roles and their crop, tint, brightness, and blur decisions. +- [build_cinematic_skin.py](scripts/build_cinematic_skin.py) is the thin command entrypoint. +- Parent `scripts/build_support.py` owns generic package and verification lifecycle operations. +- Parent `scripts/media_support.py` owns generic probing, extraction, crop, encoding, and preview primitives. + +Use [scaffold_cinematic.py](scripts/scaffold_cinematic.py) only when generating or checking the cinematic manifest without the full build. + +## Runtime inspection + +After importing the package, inspect at minimum: + +- normal workbench and collapsed navigation; +- detached Toolbar Mode and in-app floating mini chat with long conversations; +- Skills, MiniApp gallery, Agents, and Insights views; +- Settings, archived sessions, and keyboard shortcuts; +- terminal navigation, bottom terminal, files, and auxiliary panels; +- generic and dedicated dialogs; +- text over calm and bright animation frames; +- reduced-motion poster fallback. + +Record evidence in `runtime-checklist.json`. `skin-build.json` keeps `runtimeVisualInspection: false` until a real import is inspected; compiler validation does not replace runtime inspection. diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/default-palette.json b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/default-palette.json new file mode 100644 index 0000000000..4197b682c5 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/default-palette.json @@ -0,0 +1,21 @@ +{ + "schema": "bitfun.appearance.cinematic-palette", + "schemaVersion": 1, + "id": "cold-cinematic", + "colors": { + "background": "#08111f", + "surface": "#102235", + "surfaceElevated": "#182b3c", + "text": "#edf7fa", + "textSecondary": "#c5d6dc", + "textMuted": "#8aa2ab", + "textDisabled": "#60757c", + "accent": "#52e5f5", + "accentStrong": "#2fc4d7", + "accentContrast": "#041416", + "info": "#78a9ff", + "success": "#7dcc9c", + "warning": "#e2b96b", + "danger": "#e47b74" + } +} diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/palette-contract.md b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/palette-contract.md new file mode 100644 index 0000000000..ad5edb3ea4 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/palette-contract.md @@ -0,0 +1,37 @@ +# Cinematic palette contract + +Use a semantic palette to generate materials and renderer settings. Palette +files use this shape: + +```json +{ + "schema": "bitfun.appearance.cinematic-palette", + "schemaVersion": 1, + "id": "cold-cinematic", + "colors": { + "background": "#08111f", + "surface": "#102235", + "surfaceElevated": "#182b3c", + "text": "#edf7fa", + "textSecondary": "#c5d6dc", + "textMuted": "#8aa2ab", + "textDisabled": "#60757c", + "accent": "#52e5f5", + "accentStrong": "#2fc4d7", + "accentContrast": "#041416", + "info": "#78a9ff", + "success": "#7dcc9c", + "warning": "#e2b96b", + "danger": "#e47b74" + } +} +``` + +Every color is required and must be a six-digit hex value. `background`, +`surface`, and `surfaceElevated` drive translucent materials. `accent` drives +focus, borders, and primary highlights. Semantic status colors remain distinct +from the primary accent. + +Prefer colors sampled from the source artwork. Preserve readable contrast and +keep Monaco and xterm backgrounds opaque. The scaffold derives alpha values; +do not put alpha channels in the palette. diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/style-playbook.md b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/style-playbook.md new file mode 100644 index 0000000000..f68735e107 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/style-playbook.md @@ -0,0 +1,145 @@ +# Cinematic Animated Wallpaper Playbook + +## Style identity + +Use one animated character wallpaper as the visual source of truth: + +- complete motion in the main workspace; +- a tighter static character crop in floating chat; +- one expressive still frame for every sidebar; +- darkened face, weapon, clothing, energy, or environmental details in cards and dialogs; +- source-derived deep surfaces, a distinct primary accent, and separate semantic status colors; +- no obvious borders and no layout changes. + +Prefer a frame near the visual climax for still artwork. Keep source composition intact for the main background. Use the bundled cold palette only as a fallback; artwork with a strong red, gold, green, or neutral identity should drive its own semantic palette. + +## Asset roles + +| Asset | Motion | Placement | Treatment | +| --- | --- | --- | --- | +| `background.webm` | complete animation | top-level `backgroundMedia` | host-managed, source-faithful, cover | +| `floating.webp` | one frame | `toolbar-mode.root`, `floating-mini-chat.panel` | centered character crop | +| `sidebar.webp` | one frame | all registered sidebar owners | portrait crop, moderately darkened | +| `card-detail.webp` | one frame | compact cards, messages, terminal strip | lower/detail crop, strongly darkened | +| `card-portrait.webp` | one frame | hero cards, MiniApp/Agent cards | face/character crop, strongly darkened | +| `dialog.webp` | one frame | modal and dedicated dialog roots | wide crop, strongly darkened | +| `preview.webp` | one frame | package preview | representative full frame | +| `asset-preview-sheet.png` | generated report | outside the package | labeled overview of all six static roles | + +Keep `background.webm` at or below 64 MiB, every ordinary image at or below 16 MiB, `preview.webp` at or below 4 MiB, and the archive at or below 96 MiB. The host also limits background video to 60 seconds, 4096 pixels per side, and 9 million pixels. Prefer WebM for cross-platform codec availability; final BitFun import verifies actual media metadata and host playback support. + +## Surface matrix + +Re-query every id before use. + +| Purpose | Registered owners | +| --- | --- | +| complete animation | top-level `backgroundMedia`; keep scene `workbench.workspace` transparent | +| expanded/static navigation | scene `workbench.navArea`, component `nav-panel.root` | +| collapsed navigation control | transparent scene `workbench.collapsedNav`, translucent collapsed state on `nav-bar.root` | +| Skills sidebar | scene `skills.sidebar` | +| Settings sidebar | component `settings-nav.root` | +| terminal navigation | component `shell-nav.root` | +| file/auxiliary sidebars | `files-panel.root`, `flexible-panel.root`, `btw-session-panel.root`, `aux-pane.root`, scene `session.auxiliary` | +| bottom terminal art | `bottom-terminal-pane.root` with `card-detail.webp` | +| detached Toolbar Mode still artwork | `toolbar-mode.root` | +| detached Toolbar Mode inner veil | `toolbar-mode.content` | +| in-app floating mini-chat still artwork | `floating-mini-chat.panel` | +| shared floating chat transparency | `floating-mini-chat.body`, `modern-flow-chat.root`, `modern-flow-chat.messages`, `virtual-message-list.root` | +| conversation turn navigation | `flow-chat-turn-rail.root`, `.list`, `.item`, `.bar`, and tooltip Parts | +| copyable command text | `copyable-text-preview.root`, `.empty`, tooltip Parts, and `.copyAction` | +| settings transparency | `config.root`, `config.content`, `config.contentInner`, `archived-sessions-config.root`, `keyboard-shortcuts.root`, `keyboard-shortcuts.content` | +| translucent settings cards | `config.sectionBody` | +| Insights transparency | scene `insights.root`, `insights.content` | +| image-led cards | `mini-app-card.root`, `agent-card.root`, `core-agent-card.root`, `skill-card.root`, `user-message-item.root`, `flow-chat-card.root` | +| generic dialog | `modal.dialog` | +| dedicated dialogs | registered dialog/modal root or body Parts returned by the contract | + +## Cascade rules + +Host styles often use the `background` shorthand. A normal Appearance `backgroundColor` or `backgroundImage` declaration can be reset by that shorthand depending on runtime order and specificity. + +Use `cascade: "override"` only on the registered Part that owns the conflicting paint. Typical owners for this style: + +- `toolbar-mode.root` and `.content`; +- `nav-bar.root` when its host background shorthand hides the expanded navigation art or the collapsed glass state; +- `workbench.collapsedNav` when an earlier package applied sidebar artwork to the small floating owner; +- `floating-mini-chat.panel` and `.body`; +- `modern-flow-chat.root` and `.messages`; +- `virtual-message-list.root`; +- `config.root`, `.content`, `.contentInner`, and `.sectionBody`; +- `archived-sessions-config.root`; +- `keyboard-shortcuts.root` and `.content`; +- `insights.content`; +- `chat-pane.root` when the host scene background remains opaque. + +Do not use override for layout properties. The compiler only marks descriptor-approved forceable paint properties as important. + +## Material recipe + +Use reusable materials instead of repeating paint blocks: + +- `panel`: translucent deep surface plus 4-8 px backdrop blur; +- `card`: translucent surface plus a soft shadow; +- `control`: slightly denser surface, small existing radius, restrained glow; +- `detail-card`: `card-detail.webp`, cover/center, dark surface, existing radius; +- `portrait-card`: `card-portrait.webp`, cover/center, dark surface, existing radius; +- `dialog-art`: `dialog.webp`, cover/center, dense surface, existing dialog radius; +- `popup`: near-opaque surface for menus that need reliable readability; +- `code`: opaque surface for code and terminal text. + +Do not add explicit border widths. When a host border remains visually strong, override only `borderColor` with transparent or a very low-alpha accepted color. + +## Renderer palette + +Define source-specific semantic colors using [palette-contract.md](palette-contract.md). The scaffold keeps text and accent tokens opaque while deriving translucent scene, glass, card, and border values. + +For the default cold cinematic fallback, representative generated values are: + +```json +{ + "--bf-appearance-token-color-bg-scene": "rgba(8, 17, 31, 0.08)", + "--bf-appearance-token-color-bg-elevated": "rgba(24, 43, 60, 0.72)", + "--bf-appearance-token-element-bg-subtle": "rgba(11, 25, 40, 0.52)", + "--bf-appearance-token-element-bg-soft": "rgba(16, 34, 53, 0.60)", + "--bf-appearance-token-element-bg-base": "rgba(22, 43, 61, 0.66)", + "--bf-appearance-token-element-bg-hover": "rgba(23, 54, 74, 0.74)", + "--bf-appearance-token-border-subtle": "rgba(82, 229, 245, 0.08)", + "--bf-appearance-token-border-base": "rgba(82, 229, 245, 0.12)", + "--bf-appearance-token-border-medium": "rgba(82, 229, 245, 0.18)" +} +``` + +Tune the RGB values to the source palette while preserving the alpha hierarchy. + +Insights report cards currently derive their local surfaces from `color-bg-elevated` and element/border tokens. They do not expose individual registered Parts. Keep `insights.content` transparent and use these tokens for glass-card behavior. + +## Runtime failure diagnosis + +| Symptom | Likely cause | Fix | +| --- | --- | --- | +| detached floating window has no artwork | the package styled `floating-mini-chat`, but the window is `toolbar-mode` | apply static `floating.webp` to `toolbar-mode.root` with override and keep `.content` translucent | +| in-app floating mini-chat has no artwork | host `background` shorthand reset the asset | add override to the panel and transparent inner chat owners | +| main video is hidden | `workbench.workspace` or another scene owner still paints an opaque background | make the registered workspace and structural scene owners transparent; keep the video only in top-level `backgroundMedia` | +| main video shows a still image | reduced motion is enabled or video playback/codec validation failed | confirm OS/browser motion preference, inspect import errors, and use WebM VP9 with `preview.webp` as poster | +| collapsed navigation has an opaque 80x40 block | child `nav-bar.root` paints `color-bg-primary` | force the base root transparent and use a low-alpha collapsed-state glass material | +| collapsed navigation looks like a pasted image tile | `sidebar.webp` was applied to the small `workbench.collapsedNav` owner | remove the image, keep the scene owner transparent, and let the collapsed `nav-bar.root` provide quiet glass contrast | +| whole Insights history has an image | artwork was applied to `insights.content` | make content transparent; style cards through tokens | +| archived sessions or shortcuts are opaque | specialized ConfigPage root replaced the generic `config.root` attribute | override the specialized root Part directly | +| background disappears only inside chat | `modern-flow-chat.root` still paints `color-bg-scene` | make token translucent and/or force root transparent | +| chat input has a second rounded outer frame | `control` or card material was applied to the structural `chat-input.container` wrapper | remove wrapper decoration and leave `chat-input.box` as the single visual input surface | +| project validates but runtime is wrong | validator checked structure, not cascade outcome | import and inspect screenshots; then add the smallest paint override | + +## Runtime acceptance checklist + +- Main workspace animation remains visible during calm and bright frames. +- Reduced-motion mode shows the poster without autoplaying video. +- Text remains readable without making every surface opaque. +- Sidebars use still artwork, never independent animation. +- Expanded navigation remains transparent over its sidebar artwork; collapsed navigation uses quiet glass without a cropped image tile. +- Detached Toolbar Mode and in-app floating mini-chat use the intentional static character crop behind chat content and input chrome. +- Insights page background is transparent; individual cards remain distinct. +- Archived sessions and keyboard shortcuts expose the workspace through page gaps. +- No component changes size or position when the skin is applied. +- No visible one-pixel frame is introduced by the skin. +- Chat input has one visual surface; structural wrappers remain undecorated. diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/surface-plan.json b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/surface-plan.json new file mode 100644 index 0000000000..34ce91c8c6 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/references/surface-plan.json @@ -0,0 +1,1154 @@ +{ + "schema": "bitfun.appearance.style-surface-plan", + "schemaVersion": 1, + "scope": "example-style-selection", + "styleId": "cinematic-animated-wallpaper", + "validatedAgainstRegistryRevision": "7fdf35b7be1c2693e33caae122dc970fb953865b", + "scenes": { + "workbench": { + "parts": { + "workspace": { + "materials": [ + "workspace-art" + ], + "decorationIntent": "flat" + }, + "navArea": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + }, + "collapsedNav": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "sceneArea": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "viewport": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "viewportClip": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "scene": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + } + } + }, + "session": { + "parts": { + "root": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "main": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "chat": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "auxiliary": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + } + } + }, + "skills": { + "parts": { + "root": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "content": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "main": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "sidebar": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + } + } + }, + "settings": { + "parts": { + "root": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "content": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + } + } + }, + "insights": { + "parts": { + "root": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "content": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + } + } + }, + "agents": { + "parts": { + "root": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + } + } + }, + "miniapp-gallery": { + "parts": { + "root": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + } + } + } + }, + "components": { + "nav-panel": { + "parts": { + "root": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + }, + "content": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "mainLayer": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "sceneLayer": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "sceneContent": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "footerMenu": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + }, + "workspaceMenu": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + } + } + }, + "nav-bar": { + "parts": { + "root": { + "cascade": "override", + "materials": [ + "transparent" + ], + "states": { + "collapsed": { + "backgroundColor": { + "kind": "palette", + "role": "surface", + "alpha": 0.52 + }, + "backdropBlur": { + "kind": "px", + "value": 8 + }, + "borderColor": { + "kind": "transparent" + } + } + }, + "decorationIntent": "flat" + } + } + }, + "settings-nav": { + "parts": { + "root": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + } + } + }, + "shell-nav": { + "parts": { + "root": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + }, + "menu": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + } + } + }, + "files-panel": { + "parts": { + "root": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + } + } + }, + "flexible-panel": { + "parts": { + "root": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + }, + "code": { + "materials": [ + "code" + ], + "decorationIntent": "flat" + }, + "terminal": { + "materials": [ + "code" + ], + "decorationIntent": "flat" + } + } + }, + "btw-session-panel": { + "parts": { + "root": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + } + } + }, + "aux-pane": { + "parts": { + "root": { + "materials": [ + "sidebar-art" + ], + "decorationIntent": "flat" + } + } + }, + "bottom-terminal-pane": { + "parts": { + "root": { + "materials": [ + "detail-card" + ], + "decorationIntent": "flat" + } + } + }, + "toolbar-mode": { + "parts": { + "root": { + "cascade": "override", + "materials": [ + "floating-art" + ], + "decorationIntent": "flat" + }, + "content": { + "cascade": "override", + "materials": [ + "panel" + ], + "decorationIntent": "flat" + }, + "sessionSurface": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "overflowMenu": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + } + } + }, + "floating-mini-chat": { + "parts": { + "panel": { + "cascade": "override", + "materials": [ + "floating-art" + ], + "decorationIntent": "framed" + }, + "body": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "header": { + "materials": [ + "panel" + ], + "decorationIntent": "flat" + }, + "trigger": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + }, + "pending": { + "materials": [ + "panel" + ], + "decorationIntent": "flat" + } + } + }, + "modern-flow-chat": { + "parts": { + "root": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "messages": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + } + } + }, + "virtual-message-list": { + "parts": { + "root": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + } + } + }, + "chat-pane": { + "parts": { + "root": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + } + } + }, + "chat-input": { + "parts": { + "container": { + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "box": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + }, + "commandPicker": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + }, + "boostMenu": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + }, + "boostSubmenuPanel": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + } + } + }, + "config": { + "parts": { + "root": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "content": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "contentInner": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "sectionBody": { + "cascade": "override", + "materials": [ + "card" + ], + "decorationIntent": "framed" + }, + "collectionItem": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + }, + "modelSelectionDropdown": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + } + } + }, + "archived-sessions-config": { + "parts": { + "root": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "row": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + } + } + }, + "keyboard-shortcuts": { + "parts": { + "root": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "content": { + "cascade": "override", + "materials": [ + "transparent" + ], + "decorationIntent": "flat" + }, + "item": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + }, + "keyBadge": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + } + } + }, + "mini-app-card": { + "parts": { + "root": { + "materials": [ + "portrait-card" + ], + "decorationIntent": "framed" + } + } + }, + "agent-card": { + "parts": { + "root": { + "materials": [ + "portrait-card" + ], + "decorationIntent": "framed" + } + } + }, + "core-agent-card": { + "parts": { + "root": { + "materials": [ + "portrait-card" + ], + "decorationIntent": "framed" + } + } + }, + "skill-card": { + "parts": { + "root": { + "materials": [ + "portrait-card" + ], + "decorationIntent": "framed" + } + } + }, + "user-message-item": { + "parts": { + "content": { + "materials": [ + "detail-card" + ], + "decorationIntent": "framed" + } + } + }, + "flow-chat-card": { + "parts": { + "root": { + "materials": [ + "detail-card" + ], + "decorationIntent": "framed" + } + } + }, + "modal": { + "parts": { + "overlay": { + "base": { + "backgroundColor": { + "kind": "palette", + "role": "background", + "alpha": 0.62 + } + }, + "decorationIntent": "flat" + }, + "dialog": { + "cascade": "override", + "materials": [ + "dialog-art" + ], + "decorationIntent": "framed" + } + } + }, + "confirm-dialog": { + "parts": { + "root": { + "materials": [ + "dialog-art" + ], + "decorationIntent": "framed" + } + } + }, + "input-dialog": { + "parts": { + "root": { + "materials": [ + "dialog-art" + ], + "decorationIntent": "framed" + } + } + }, + "gallery-detail-modal": { + "parts": { + "root": { + "materials": [ + "dialog-art" + ], + "decorationIntent": "framed" + } + } + }, + "mcp-interaction-dialog": { + "parts": { + "root": { + "materials": [ + "dialog-art" + ], + "decorationIntent": "framed" + } + } + }, + "remote-connect-disclaimer": { + "parts": { + "root": { + "materials": [ + "dialog-art" + ], + "decorationIntent": "framed" + } + } + }, + "scheduled-jobs-modal": { + "parts": { + "body": { + "materials": [ + "dialog-art" + ], + "decorationIntent": "framed" + } + } + }, + "session-menu": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "trigger": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + }, + "dropdown": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + }, + "item": { + "states": { + "active": { + "opacity": { + "kind": "number", + "value": 1 + } + } + } + } + } + }, + "composer-voice-input": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat", + "states": { + "disabled": { + "opacity": { + "kind": "number", + "value": 0.52 + } + } + } + }, + "pill": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + }, + "timeline": { + "materials": [ + "panel" + ], + "decorationIntent": "flat" + }, + "action": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + } + } + }, + "miniapp-bubble-welcome": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "workspace": { + "materials": [ + "panel" + ], + "decorationIntent": "flat" + }, + "suggestion": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + } + } + }, + "session-title-config": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "enableControl": { + "materials": [ + "panel" + ], + "decorationIntent": "flat" + }, + "modelControl": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + } + } + }, + "assistant-card": { + "parts": { + "root": { + "materials": [ + "portrait-card" + ], + "decorationIntent": "framed", + "states": { + "busy": { + "opacity": { + "kind": "number", + "value": 0.82 + } + } + } + }, + "configure": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + }, + "newSession": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + } + } + }, + "workspace-item": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat", + "states": { + "dragging": { + "opacity": { + "kind": "number", + "value": 0.72 + } + } + } + }, + "card": { + "materials": [ + "card" + ], + "decorationIntent": "framed", + "contexts": [ + { + "when": { + "facets": { + "variant": "assistant" + } + }, + "style": { + "backgroundImage": { + "kind": "asset", + "assetId": "card-portrait" + }, + "backgroundSize": "cover", + "backgroundPosition": "center", + "backgroundRepeat": "no-repeat" + } + } + ] + }, + "menuPopover": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + }, + "indexPanel": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + } + } + }, + "external-mcp-overview": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "summary": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + }, + "statusBadge": { + "materials": [ + "control" + ], + "decorationIntent": "framed", + "states": { + "error": { + "opacity": { + "kind": "number", + "value": 0.88 + } + }, + "muted": { + "opacity": { + "kind": "number", + "value": 0.62 + } + } + } + }, + "serverDetails": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + }, + "importOption": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + }, + "empty": { + "materials": [ + "panel" + ], + "decorationIntent": "flat" + } + } + }, + "miniapp-customize-panel": { + "parts": { + "root": { + "materials": [ + "panel" + ], + "decorationIntent": "flat", + "states": { + "busy": { + "opacity": { + "kind": "number", + "value": 0.94 + } + } + } + }, + "notice": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + }, + "request": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + }, + "chat": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "footer": { + "materials": [ + "panel" + ], + "decorationIntent": "flat" + } + } + }, + "user-message-edit-composer": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat", + "states": { + "submitting": { + "opacity": { + "kind": "number", + "value": 0.76 + } + } + } + }, + "input": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + }, + "action": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + } + } + }, + "voice-input-diagnostics": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "deviceSelect": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + }, + "diagnosticAction": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + }, + "level": { + "materials": [ + "panel" + ], + "decorationIntent": "flat" + }, + "recognitionTest": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + }, + "result": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + }, + "error": { + "materials": [ + "card" + ], + "decorationIntent": "framed" + } + } + }, + "flow-chat-turn-rail": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "list": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "item": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "tooltipContent": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + } + } + }, + "copyable-text-preview": { + "parts": { + "root": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "empty": { + "base": { + "backgroundColor": { + "kind": "transparent" + } + }, + "decorationIntent": "flat" + }, + "tooltipContent": { + "materials": [ + "popup" + ], + "decorationIntent": "framed" + }, + "copyAction": { + "materials": [ + "control" + ], + "decorationIntent": "framed" + } + } + } + } +} diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/build_assets.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/build_assets.py new file mode 100644 index 0000000000..f45fbf8e9f --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/build_assets.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +from PIL import ImageFilter + + +SKILL_ROOT = Path(__file__).resolve().parents[3] +sys.path.insert(0, str(SKILL_ROOT / "scripts")) + +import media_support as media # noqa: E402 + + +PREVIEW_ASSET_NAMES = ( + "preview.webp", + "floating.webp", + "sidebar.webp", + "card-detail.webp", + "card-portrait.webp", + "dialog.webp", +) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Build cinematic animated-wallpaper skin assets.") + parser.add_argument("--source", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True, help="Skin root containing package/assets.") + parser.add_argument("--frame-index", type=int, default=-1, help="Still frame; -1 selects the midpoint.") + parser.add_argument("--frame-time", type=float, help="Still time in seconds for video sources; defaults to midpoint.") + parser.add_argument("--frame-duration-ms", type=int, default=media.DEFAULT_FRAME_DURATION_MS) + parser.add_argument("--video-quality", choices=sorted(media.VIDEO_QUALITY_MODES), default="auto") + parser.add_argument("--video-crf", type=int, help="Explicit CRF override for reproducibility or manual tuning.") + parser.add_argument("--static-quality-mode", choices=sorted(media.STATIC_QUALITY_MODES), default="auto") + parser.add_argument("--static-quality", type=int, help="Explicit lossy WebP quality override.") + parser.add_argument("--tint-color", type=media.parse_color, default=media.parse_color("#050f1d")) + parser.add_argument("--contact-only", action="store_true") + parser.add_argument("--static-only", action="store_true", help="Build derived stills without encoding background.webm.") + parser.add_argument("--floating-crop", type=media.parse_crop, default=media.parse_crop("0.18,0,0.82,1")) + parser.add_argument("--floating-size", type=media.parse_size, default=media.parse_size("820x720")) + parser.add_argument("--sidebar-crop", type=media.parse_crop, default=media.parse_crop("0.64,0,1,1")) + parser.add_argument("--sidebar-size", type=media.parse_size, default=media.parse_size("460x720")) + parser.add_argument("--detail-crop", type=media.parse_crop, default=media.parse_crop("0.41,0.54,1,1")) + parser.add_argument("--detail-size", type=media.parse_size, default=media.parse_size("760x330")) + parser.add_argument("--portrait-crop", type=media.parse_crop, default=media.parse_crop("0.21,0.06,0.80,0.79")) + parser.add_argument("--portrait-size", type=media.parse_size, default=media.parse_size("760x525")) + parser.add_argument("--dialog-crop", type=media.parse_crop, default=media.parse_crop("0.09,0.28,0.91,1")) + parser.add_argument("--dialog-size", type=media.parse_size, default=media.parse_size("1040x515")) + return parser.parse_args() + + +def build(args: argparse.Namespace) -> None: + source = args.source.resolve() + output = args.output.resolve() + if not source.is_file(): + raise FileNotFoundError(f"Source image does not exist: {source}") + if args.video_crf is not None and not 0 <= args.video_crf <= 63: + raise ValueError("video CRF must be between 0 and 63") + if args.static_quality is not None and not 1 <= args.static_quality <= 100: + raise ValueError("static quality must be between 1 and 100") + if args.frame_duration_ms <= 0: + raise ValueError("frame duration must be positive") + + is_video = source.suffix.lower() in media.VIDEO_SUFFIXES + duration = media.probe_video_duration(source) if is_video else None + if is_video: + frames, labels = media.load_video_contact_frames(source, duration) + else: + frames, _, _ = media.load_image_frames(source, args.frame_duration_ms) + labels = None + + output.mkdir(parents=True, exist_ok=True) + contact_sheet = output / "contact-sheet.png" + media.create_contact_sheet(frames, contact_sheet, labels=labels) + if args.contact_only: + print(json.dumps({ + "contactSheet": str(contact_sheet), + "frames": len(frames), + "durationSeconds": duration, + }, indent=2)) + return + + assets = output / "package" / "assets" + assets.mkdir(parents=True, exist_ok=True) + video_encoding = None + if not args.static_only: + video_encoding = media.encode_vp9_webm( + source, + assets / "background.webm", + quality=args.video_quality, + crf=args.video_crf, + ) + + if is_video: + frame_time = duration / 2 if args.frame_time is None else args.frame_time + if not 0 <= frame_time <= duration: + raise ValueError(f"frame time {frame_time} is outside 0..{duration}") + still = media.extract_video_frame(source, frame_time) + selected_frame: int | float = frame_time + else: + if args.frame_time is not None: + raise ValueError("--frame-time is supported only for video sources") + frame_index = len(frames) // 2 if args.frame_index < 0 else args.frame_index + if frame_index >= len(frames): + raise ValueError(f"frame index {frame_index} is outside 0..{len(frames) - 1}") + still = frames[frame_index] + selected_frame = frame_index + + tint_red, tint_green, tint_blue = args.tint_color + tint = (tint_red, tint_green, tint_blue) + + floating = media.fit_crop(still, args.floating_crop, args.floating_size) + floating = media.darken(floating, 0.58, (*tint, 36)) + static_encoding: dict[str, object] = {} + + def encode_asset(name: str, image, *, preview: bool = False) -> None: + static_encoding[name] = media.encode_webp( + image, + assets / name, + mode=args.static_quality_mode, + quality=args.static_quality, + max_bytes=media.MAX_PREVIEW_BYTES if preview else media.MAX_IMAGE_BYTES, + ) + + encode_asset("floating.webp", floating) + + sidebar = media.fit_crop(still, args.sidebar_crop, args.sidebar_size) + sidebar = media.darken(sidebar, 0.62, (*tint, 28)) + encode_asset("sidebar.webp", sidebar) + + detail = media.fit_crop(still, args.detail_crop, args.detail_size) + detail = media.darken(detail, 0.42, (*tint, 72)).filter(ImageFilter.GaussianBlur(0.35)) + encode_asset("card-detail.webp", detail) + + portrait = media.fit_crop(still, args.portrait_crop, args.portrait_size) + portrait = media.darken(portrait, 0.34, (*tint, 92)).filter(ImageFilter.GaussianBlur(0.55)) + encode_asset("card-portrait.webp", portrait) + + dialog = media.fit_crop(still, args.dialog_crop, args.dialog_size) + dialog = media.darken(dialog, 0.38, (*tint, 90)).filter(ImageFilter.GaussianBlur(0.45)) + encode_asset("dialog.webp", dialog) + + encode_asset("preview.webp", still, preview=True) + asset_preview_sheet = output / "asset-preview-sheet.png" + media.create_asset_preview_sheet(assets, asset_preview_sheet, PREVIEW_ASSET_NAMES) + print(json.dumps({ + "source": str(source), + "selectedFrame": selected_frame, + "selectedFrameKind": "seconds" if is_video else "index", + "tintColor": "#{:02x}{:02x}{:02x}".format(*args.tint_color), + "assetPreviewSheet": str(asset_preview_sheet), + "encoding": { + "video": video_encoding, + "static": static_encoding, + }, + "assets": media.inspect_assets(assets), + }, indent=2)) + + +if __name__ == "__main__": + build(parse_args()) diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/build_cinematic_skin.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/build_cinematic_skin.py new file mode 100644 index 0000000000..5acbf7f1f6 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/build_cinematic_skin.py @@ -0,0 +1,483 @@ +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +import zipfile +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +SCRIPT_DIR = Path(__file__).resolve().parent +SKILL_ROOT = SCRIPT_DIR.parents[2] +sys.path.insert(0, str(SKILL_ROOT / "scripts")) + +import build_support as build # noqa: E402 +import media_support as media # noqa: E402 +from cinematic_recipe import ( # noqa: E402 + DEFAULT_PALETTE, + DEFAULT_SURFACE_PLAN, + CinematicContractError, + load_palette, +) + + +ASSET_BUILDER = SCRIPT_DIR / "build_assets.py" +SCAFFOLD = SCRIPT_DIR / "scaffold_cinematic.py" +BUILD_CONFIG_NAME = "skin-build.json" +RUNTIME_CHECKLIST_NAME = "runtime-checklist.json" +BUILD_CONFIG_SCHEMA = "bitfun.appearance.recipe-build" +RECIPE_ID = "cinematic-animated-wallpaper" + + +@dataclass +class BuildOptions: + source: Path + output: Path + appearance_id: str + name: str + version: str + mode: str + author: str | None + description: str | None + frame_time: float | None + frame_index: int + video_quality: str + video_crf: int | None + static_quality_mode: str + static_quality: int | None + tint_color: str | None + floating_crop: str + floating_size: str + sidebar_crop: str + sidebar_size: str + detail_crop: str + detail_size: str + portrait_crop: str + portrait_size: str + dialog_crop: str + dialog_size: str + palette: Path + surface_plan: Path + bitfun_repo: Path | None + allow_warnings: bool + force: bool + + +def asset_command(options: BuildOptions) -> list[str]: + command = [ + sys.executable, + str(ASSET_BUILDER), + "--source", + str(options.source), + "--output", + str(options.output), + "--frame-index", + str(options.frame_index), + "--video-quality", + options.video_quality, + "--static-quality-mode", + options.static_quality_mode, + "--floating-crop", + options.floating_crop, + "--floating-size", + options.floating_size, + "--sidebar-crop", + options.sidebar_crop, + "--sidebar-size", + options.sidebar_size, + "--detail-crop", + options.detail_crop, + "--detail-size", + options.detail_size, + "--portrait-crop", + options.portrait_crop, + "--portrait-size", + options.portrait_size, + "--dialog-crop", + options.dialog_crop, + "--dialog-size", + options.dialog_size, + ] + if options.video_crf is not None: + command.extend(["--video-crf", str(options.video_crf)]) + if options.static_quality is not None: + command.extend(["--static-quality", str(options.static_quality)]) + if options.frame_time is not None: + command.extend(["--frame-time", str(options.frame_time)]) + palette = load_palette(options.palette) + tint = options.tint_color or palette["colors"]["background"] + command.extend(["--tint-color", tint]) + return command + + +def scaffold_command(options: BuildOptions, palette: Path, surface_plan: Path, check: bool = False) -> list[str]: + command = [ + sys.executable, + str(SCAFFOLD), + "--output", + str(options.output), + "--id", + options.appearance_id, + "--name", + options.name, + "--version", + options.version, + "--mode", + options.mode, + "--palette", + str(palette), + "--surface-plan", + str(surface_plan), + ] + if options.author: + command.extend(["--author", options.author]) + if options.description: + command.extend(["--description", options.description]) + command.append("--check" if check else "--force") + return command + + +def ensure_runtime_checklist(output: Path, appearance_id: str) -> Path: + path = output / RUNTIME_CHECKLIST_NAME + checks = [ + ("workbench", "Normal workbench and collapsed navigation"), + ("floating-chat", "Toolbar Mode and in-app floating mini chat"), + ("catalogs", "Skills, MiniApp gallery, Agents, and Insights"), + ("settings", "Settings, archived sessions, and keyboard shortcuts"), + ("panels", "Terminal navigation, bottom terminal, files, and auxiliary panels"), + ("dialogs", "Generic and dedicated dialogs"), + ("motion", "Reduced-motion poster fallback"), + ("readability", "Text readability over calm and bright frames"), + ] + return build.ensure_runtime_checklist(path, appearance_id=appearance_id, checks=checks) + + +def build_skin(options: BuildOptions) -> dict[str, Any]: + options.source = options.source.resolve() + options.output = options.output.resolve() + options.palette = options.palette.resolve() + options.surface_plan = options.surface_plan.resolve() + if options.bitfun_repo is not None: + options.bitfun_repo = options.bitfun_repo.resolve() + if not options.source.is_file(): + raise FileNotFoundError(f"Source does not exist: {options.source}") + + options.output.mkdir(parents=True, exist_ok=True) + package = options.output / "package" + build.initialize_package( + package, + appearance_id=options.appearance_id, + name=options.name, + mode=options.mode, + force=options.force, + ) + asset_result = build.run_json(asset_command(options)) + + sources = options.output / "sources" + palette_copy = sources / "palette.json" + plan_copy = sources / "surface-plan.json" + build.copy_input(options.palette, palette_copy) + build.copy_input(options.surface_plan, plan_copy) + build.run(scaffold_command(options, palette_copy, plan_copy)) + + archive = options.output / f"{options.appearance_id}.bitfun-appearance" + build.validate_project(package) + build.build_archive(package, archive) + build.validate_archive(archive) + + host_report: dict[str, Any] | None = None + report_path = options.output / "host-verification.json" + if options.bitfun_repo is not None: + host_report = build.verify_host( + options.bitfun_repo, + archive, + report=report_path, + strict_warnings=not options.allow_warnings, + ) + + video = media.probe_video(package / "assets" / "background.webm") + media.validate_host_video(video) + background_compression = build.archive_member_compression(archive, "assets/background.webm") + if background_compression != zipfile.ZIP_STORED: + raise CinematicContractError("Background video must use stored ZIP compression") + + runtime_checklist = ensure_runtime_checklist(options.output, options.appearance_id) + palette = load_palette(palette_copy) + build_config = { + "schema": BUILD_CONFIG_SCHEMA, + "schemaVersion": 1, + "recipe": { + "id": RECIPE_ID, + "surfacePlanScope": "example-style-selection", + }, + "appearance": { + "id": options.appearance_id, + "name": options.name, + "version": options.version, + "mode": options.mode, + "author": options.author, + "description": options.description, + }, + "source": { + "path": str(options.source), + "bytes": options.source.stat().st_size, + "sha256": build.sha256(options.source), + }, + "assetBuild": { + "frameTime": options.frame_time, + "frameIndex": options.frame_index, + "videoQuality": options.video_quality, + "videoCrf": options.video_crf, + "staticQualityMode": options.static_quality_mode, + "staticQuality": options.static_quality, + "resolvedEncoding": asset_result.get("encoding"), + "tintColor": options.tint_color or palette["colors"]["background"], + "floatingCrop": options.floating_crop, + "floatingSize": options.floating_size, + "sidebarCrop": options.sidebar_crop, + "sidebarSize": options.sidebar_size, + "detailCrop": options.detail_crop, + "detailSize": options.detail_size, + "portraitCrop": options.portrait_crop, + "portraitSize": options.portrait_size, + "dialogCrop": options.dialog_crop, + "dialogSize": options.dialog_size, + }, + "inputs": { + "palette": {"path": "sources/palette.json", "sha256": build.sha256(palette_copy)}, + "surfacePlan": {"path": "sources/surface-plan.json", "sha256": build.sha256(plan_copy)}, + }, + "registry": build.registry_provenance(), + "host": { + "repo": str(options.bitfun_repo) if options.bitfun_repo else None, + "strictWarnings": not options.allow_warnings, + }, + "outputs": { + "archive": { + "path": archive.name, + "bytes": archive.stat().st_size, + "sha256": build.sha256(archive), + "backgroundCompression": "stored", + }, + "backgroundVideo": video, + "contactSheet": "contact-sheet.png", + "assetPreviewSheet": "asset-preview-sheet.png", + "hostReport": report_path.name if host_report else None, + "runtimeChecklist": runtime_checklist.name, + }, + "verification": { + "projectValidation": True, + "archiveValidation": True, + "hostValidation": bool(host_report and host_report.get("valid")), + "hostWarnings": host_report.get("warnings", []) if host_report else None, + "runtimeVisualInspection": False, + }, + } + config_path = options.output / BUILD_CONFIG_NAME + build.atomic_write_json(config_path, build_config) + result = { + "output": str(options.output), + "archive": str(archive), + "archiveBytes": archive.stat().st_size, + "archiveSha256": build.sha256(archive), + "hostValid": host_report.get("valid") if host_report else None, + "hostWarnings": host_report.get("warnings") if host_report else None, + "runtimeVisualInspection": False, + "buildConfig": str(config_path), + } + print(json.dumps(result, indent=2)) + return result + + +def options_from_config(output: Path, repo_override: Path | None) -> BuildOptions: + config = build.read_json(output / BUILD_CONFIG_NAME) + if config.get("schema") != BUILD_CONFIG_SCHEMA or config.get("schemaVersion") != 1: + raise CinematicContractError("Unsupported cinematic build config") + if config.get("recipe", {}).get("id") != RECIPE_ID: + raise CinematicContractError("Build config belongs to a different style recipe") + appearance = config["appearance"] + source = config["source"] + assets = config["assetBuild"] + host_repo = repo_override or (Path(config["host"]["repo"]) if config["host"].get("repo") else None) + return BuildOptions( + source=Path(source["path"]), + output=output, + appearance_id=appearance["id"], + name=appearance["name"], + version=appearance["version"], + mode=appearance["mode"], + author=appearance.get("author"), + description=appearance.get("description"), + frame_time=assets.get("frameTime"), + frame_index=assets.get("frameIndex", -1), + video_quality=assets["videoQuality"], + video_crf=assets.get("videoCrf"), + static_quality_mode=assets["staticQualityMode"], + static_quality=assets.get("staticQuality"), + tint_color=assets.get("tintColor"), + floating_crop=assets["floatingCrop"], + floating_size=assets["floatingSize"], + sidebar_crop=assets["sidebarCrop"], + sidebar_size=assets["sidebarSize"], + detail_crop=assets["detailCrop"], + detail_size=assets["detailSize"], + portrait_crop=assets["portraitCrop"], + portrait_size=assets["portraitSize"], + dialog_crop=assets["dialogCrop"], + dialog_size=assets["dialogSize"], + palette=output / config["inputs"]["palette"]["path"], + surface_plan=output / config["inputs"]["surfacePlan"]["path"], + bitfun_repo=host_repo, + allow_warnings=not config["host"].get("strictWarnings", True), + force=True, + ) + + +def check_skin(output: Path, repo_override: Path | None, skip_host: bool) -> dict[str, Any]: + output = output.resolve() + config = build.read_json(output / BUILD_CONFIG_NAME) + options = options_from_config(output, repo_override) + issues: list[str] = [] + + source = Path(config["source"]["path"]) + if not source.is_file() or build.sha256(source) != config["source"]["sha256"]: + issues.append("source hash mismatch") + for key in ("palette", "surfacePlan"): + entry = config["inputs"][key] + build.check_recorded_file(output, entry, label=key, issues=issues) + + build.run(scaffold_command(options, options.palette, options.surface_plan, check=True)) + package = output / "package" + archive = output / config["outputs"]["archive"]["path"] + build.validate_project(package) + build.validate_archive(archive) + if build.sha256(archive) != config["outputs"]["archive"]["sha256"]: + issues.append("archive hash mismatch") + + if not skip_host and options.bitfun_repo is not None: + build.verify_host( + options.bitfun_repo, + archive, + strict_warnings=not options.allow_warnings, + ) + + checklist = build.read_json(output / RUNTIME_CHECKLIST_NAME) + result = { + "output": str(output), + "valid": not issues, + "issues": issues, + "registry": build.registry_provenance(), + "runtimeInspectionStatus": checklist.get("status"), + } + print(json.dumps(result, indent=2)) + if issues: + raise CinematicContractError("Cinematic build check failed: " + ", ".join(issues)) + return result + + +def add_build_arguments(parser: argparse.ArgumentParser) -> None: + parser.add_argument("--source", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--id", required=True, dest="appearance_id") + parser.add_argument("--name", required=True) + parser.add_argument("--version", default="1.0.0") + parser.add_argument("--mode", choices=("light", "dark"), default="dark") + parser.add_argument("--author") + parser.add_argument("--description") + parser.add_argument("--frame-time", type=float) + parser.add_argument("--frame-index", type=int, default=-1) + parser.add_argument( + "--video-quality", + choices=sorted(media.VIDEO_QUALITY_MODES), + default="auto", + help="Encoding policy; auto selects the highest quality that satisfies host limits.", + ) + parser.add_argument("--video-crf", type=int, help="Explicit CRF override for reproducibility or manual tuning.") + parser.add_argument( + "--static-quality-mode", + choices=sorted(media.STATIC_QUALITY_MODES), + default="auto", + help="WebP policy; auto tries lossless before quality fallback.", + ) + parser.add_argument("--static-quality", type=int, help="Explicit lossy WebP quality override.") + parser.add_argument("--tint-color") + parser.add_argument("--floating-crop", default="0.18,0,0.82,1") + parser.add_argument("--floating-size", default="820x720") + parser.add_argument("--sidebar-crop", default="0.64,0,1,1") + parser.add_argument("--sidebar-size", default="460x720") + parser.add_argument("--detail-crop", default="0.41,0.54,1,1") + parser.add_argument("--detail-size", default="760x330") + parser.add_argument("--portrait-crop", default="0.21,0.06,0.80,0.79") + parser.add_argument("--portrait-size", default="760x525") + parser.add_argument("--dialog-crop", default="0.09,0.28,0.91,1") + parser.add_argument("--dialog-size", default="1040x515") + parser.add_argument("--palette", type=Path, default=DEFAULT_PALETTE) + parser.add_argument("--surface-plan", type=Path, default=DEFAULT_SURFACE_PLAN) + parser.add_argument("--bitfun-repo", type=Path) + parser.add_argument("--allow-warnings", action="store_true") + parser.add_argument("--force", action="store_true") + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Build, rebuild, or check a cinematic BitFun skin.") + subparsers = parser.add_subparsers(dest="command", required=True) + + contact = subparsers.add_parser("contact", help="Generate only the timestamped source contact sheet.") + contact.add_argument("--source", type=Path, required=True) + contact.add_argument("--output", type=Path, required=True) + + build = subparsers.add_parser("build", help="Run the complete cinematic package pipeline.") + add_build_arguments(build) + + rebuild = subparsers.add_parser("rebuild", help="Rebuild from skin-build.json.") + rebuild.add_argument("--output", type=Path, required=True) + rebuild.add_argument("--bitfun-repo", type=Path) + + check = subparsers.add_parser("check", help="Check reproducibility, hashes, validation, and host compatibility.") + check.add_argument("--output", type=Path, required=True) + check.add_argument("--bitfun-repo", type=Path) + check.add_argument("--skip-host-verify", action="store_true") + return parser.parse_args() + + +def namespace_to_options(args: argparse.Namespace) -> BuildOptions: + values = vars(args).copy() + values.pop("command", None) + return BuildOptions(**values) + + +def main() -> None: + args = parse_args() + try: + if args.command == "contact": + build.run([ + sys.executable, + str(ASSET_BUILDER), + "--source", + str(args.source), + "--output", + str(args.output), + "--contact-only", + ]) + elif args.command == "build": + build_skin(namespace_to_options(args)) + elif args.command == "rebuild": + build_skin(options_from_config(args.output.resolve(), args.bitfun_repo)) + else: + check_skin(args.output, args.bitfun_repo, args.skip_host_verify) + except ( + CinematicContractError, + build.BuildSupportError, + media.MediaSupportError, + FileNotFoundError, + OSError, + subprocess.CalledProcessError, + json.JSONDecodeError, + ) as error: + raise SystemExit(f"ERROR: {error}") from error + + +if __name__ == "__main__": + main() diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/cinematic_recipe.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/cinematic_recipe.py new file mode 100644 index 0000000000..37e5003965 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/cinematic_recipe.py @@ -0,0 +1,473 @@ +from __future__ import annotations + +import json +import re +import sys +from pathlib import Path +from typing import Any, Mapping + + +SKILL_ROOT = Path(__file__).resolve().parents[3] +REFERENCES = Path(__file__).resolve().parents[1] / "references" +DEFAULT_PALETTE = REFERENCES / "default-palette.json" +DEFAULT_SURFACE_PLAN = REFERENCES / "surface-plan.json" +sys.path.insert(0, str(SKILL_ROOT / "scripts")) + +import bitfun_appearance as appearance # noqa: E402 +from build_support import atomic_write_json, read_json # noqa: E402 + + +PALETTE_SCHEMA = "bitfun.appearance.cinematic-palette" +SURFACE_PLAN_SCHEMA = "bitfun.appearance.style-surface-plan" +STYLE_ID = "cinematic-animated-wallpaper" +PALETTE_KEYS = { + "background", + "surface", + "surfaceElevated", + "text", + "textSecondary", + "textMuted", + "textDisabled", + "accent", + "accentStrong", + "accentContrast", + "info", + "success", + "warning", + "danger", +} +HEX_PATTERN = re.compile(r"^#[0-9a-fA-F]{6}$") + + +class CinematicContractError(ValueError): + pass + + +def load_palette(path: Path = DEFAULT_PALETTE) -> dict[str, Any]: + palette = read_json(path) + if palette.get("schema") != PALETTE_SCHEMA or palette.get("schemaVersion") != 1: + raise CinematicContractError("Unsupported cinematic palette schema") + palette_id = palette.get("id") + if not isinstance(palette_id, str) or not re.fullmatch(r"[a-z0-9][a-z0-9-]{0,63}", palette_id): + raise CinematicContractError("Palette id must use lowercase letters, digits, and hyphens") + colors = palette.get("colors") + if not isinstance(colors, dict): + raise CinematicContractError("Palette colors must be an object") + missing = sorted(PALETTE_KEYS - set(colors)) + extra = sorted(set(colors) - PALETTE_KEYS) + if missing or extra: + details = [] + if missing: + details.append(f"missing: {', '.join(missing)}") + if extra: + details.append(f"unknown: {', '.join(extra)}") + raise CinematicContractError("Invalid palette keys (" + "; ".join(details) + ")") + invalid = sorted(key for key, value in colors.items() if not isinstance(value, str) or not HEX_PATTERN.fullmatch(value)) + if invalid: + raise CinematicContractError(f"Palette colors must be six-digit hex values: {', '.join(invalid)}") + return palette + + +def load_surface_plan(path: Path = DEFAULT_SURFACE_PLAN) -> dict[str, Any]: + plan = read_json(path) + if plan.get("schema") != SURFACE_PLAN_SCHEMA or plan.get("schemaVersion") != 1: + raise CinematicContractError("Unsupported cinematic surface plan schema") + if plan.get("scope") != "example-style-selection" or plan.get("styleId") != STYLE_ID: + raise CinematicContractError("Cinematic surface plan must declare its example style scope") + if not isinstance(plan.get("validatedAgainstRegistryRevision"), str): + raise CinematicContractError("Cinematic surface plan must record its validated registry revision") + if not isinstance(plan.get("components"), dict) or not isinstance(plan.get("scenes"), dict): + raise CinematicContractError("Surface plan must define component and scene objects") + return plan + + +def hex_rgb(value: str) -> tuple[int, int, int]: + return int(value[1:3], 16), int(value[3:5], 16), int(value[5:7], 16) + + +def color_value(value: str, alpha: float = 1) -> dict[str, Any]: + red, green, blue = hex_rgb(value) + return {"kind": "rgb", "r": red, "g": green, "b": blue, "a": alpha} + + +def rgba(value: str, alpha: float, spaced: bool = True) -> str: + red, green, blue = hex_rgb(value) + separator = ", " if spaced else "," + return f"rgba({red}{separator}{green}{separator}{blue}{separator}{alpha:.2f})" + + +def rgb_triplet(value: str, spaced: bool = True) -> str: + separator = ", " if spaced else "," + return separator.join(str(channel) for channel in hex_rgb(value)) + + +def resolve_palette_values(value: Any, colors: Mapping[str, str]) -> Any: + if isinstance(value, dict): + if value.get("kind") == "palette": + role = value.get("role") + alpha = value.get("alpha", 1) + if role not in colors: + raise CinematicContractError(f"Unknown palette role in surface plan: {role}") + if not isinstance(alpha, (int, float)) or isinstance(alpha, bool) or not 0 <= alpha <= 1: + raise CinematicContractError(f"Invalid palette alpha for role {role}") + return color_value(colors[role], float(alpha)) + return {key: resolve_palette_values(child, colors) for key, child in value.items()} + if isinstance(value, list): + return [resolve_palette_values(child, colors) for child in value] + return value + + +def shadow(y: int, blur: int, spread: int, alpha: float) -> dict[str, Any]: + return { + "kind": "shadow", + "x": {"kind": "zero"}, + "y": {"kind": "px", "value": y}, + "blur": {"kind": "px", "value": blur}, + "spread": {"kind": "px", "value": spread}, + "color": {"kind": "rgb", "r": 0, "g": 0, "b": 0, "a": alpha}, + } + + +def image_material(asset_id: str, background: str, alpha: float, role: str) -> dict[str, Any]: + return { + "visualRole": role, + "style": { + "backgroundColor": color_value(background, alpha), + "backgroundImage": {"kind": "asset", "assetId": asset_id}, + "backgroundSize": "cover", + "backgroundPosition": "center", + "backgroundRepeat": "no-repeat", + "borderColor": {"kind": "transparent"}, + }, + } + + +def build_materials(colors: Mapping[str, str]) -> dict[str, Any]: + accent = colors["accent"] + background = colors["background"] + return { + "transparent": { + "visualRole": "continuous-surface", + "style": { + "backgroundColor": {"kind": "transparent"}, + "borderColor": {"kind": "transparent"}, + }, + }, + "workspace-art": { + "visualRole": "workspace", + "style": {"backgroundColor": {"kind": "transparent"}}, + }, + "sidebar-art": image_material("sidebar", background, 0.72, "panel"), + "floating-art": image_material("floating", background, 0.34, "workspace"), + "panel": { + "visualRole": "panel", + "style": { + "backgroundColor": color_value(colors["surface"], 0.48), + "backdropBlur": {"kind": "px", "value": 8}, + "borderColor": color_value(accent, 0.10), + }, + }, + "card": { + "visualRole": "card", + "style": { + "backgroundColor": color_value(colors["surfaceElevated"], 0.68), + "backdropBlur": {"kind": "px", "value": 5}, + "borderColor": color_value(accent, 0.10), + "boxShadow": [shadow(10, 28, -12, 0.48)], + }, + }, + "control": { + "visualRole": "control", + "style": { + "backgroundColor": color_value(colors["surfaceElevated"], 0.82), + "backdropBlur": {"kind": "px", "value": 6}, + "borderColor": color_value(accent, 0.13), + }, + }, + "detail-card": image_material("card-detail", background, 0.78, "card"), + "portrait-card": image_material("card-portrait", background, 0.80, "card"), + "dialog-art": { + **image_material("dialog", background, 0.90, "dialog"), + "style": { + **image_material("dialog", background, 0.90, "dialog")["style"], + "borderColor": color_value(accent, 0.12), + "boxShadow": [shadow(18, 52, -16, 0.72)], + }, + }, + "popup": { + "visualRole": "popup", + "style": { + "backgroundColor": color_value(colors["surface"], 0.96), + "borderColor": color_value(accent, 0.12), + }, + }, + "code": { + "visualRole": "content", + "style": { + "backgroundColor": {"kind": "hex", "value": background}, + "borderColor": {"kind": "transparent"}, + }, + }, + } + + +def build_renderers(colors: Mapping[str, str], appearance_id: str) -> dict[str, Any]: + background = colors["background"] + surface = colors["surface"] + elevated = colors["surfaceElevated"] + accent = colors["accent"] + return { + "css-tokens": { + "version": 1, + "settings": { + "background": rgba(background, 0.18), + "tokens": { + "--bf-appearance-token-color-bg-primary": rgba(background, 0.78), + "--bf-appearance-token-color-bg-secondary": rgba(surface, 0.72), + "--bf-appearance-token-color-bg-tertiary": rgba(elevated, 0.76), + "--bf-appearance-token-color-bg-elevated": rgba(elevated, 0.74), + "--bf-appearance-token-color-bg-scene": rgba(background, 0.08), + "--bf-appearance-token-color-bg-workbench": rgba(background, 0.08), + "--bf-appearance-token-element-bg-subtle": rgba(surface, 0.50), + "--bf-appearance-token-element-bg-soft": rgba(surface, 0.58), + "--bf-appearance-token-element-bg-base": rgba(elevated, 0.66), + "--bf-appearance-token-element-bg-medium": rgba(elevated, 0.70), + "--bf-appearance-token-element-bg-hover": rgba(elevated, 0.78), + "--bf-appearance-token-color-text-primary": colors["text"], + "--bf-appearance-token-color-text-secondary": colors["textSecondary"], + "--bf-appearance-token-color-text-muted": colors["textMuted"], + "--bf-appearance-token-color-text-disabled": colors["textDisabled"], + "--bf-appearance-token-color-accent-500": accent, + "--bf-appearance-token-color-accent-500-rgb": rgb_triplet(accent), + "--bf-appearance-token-color-accent-600": colors["accentStrong"], + "--bf-appearance-token-color-cyan-500": colors["info"], + "--bf-appearance-token-border-subtle": rgba(accent, 0.08), + "--bf-appearance-token-border-base": rgba(accent, 0.12), + "--bf-appearance-token-border-medium": rgba(accent, 0.18), + "--bf-appearance-token-border-strong": rgba(accent, 0.28), + "--bf-appearance-token-glass-bg-base": rgba(surface, 0.58), + "--bf-appearance-token-glass-bg-hover": rgba(elevated, 0.68), + "--bf-appearance-token-glass-bg-active": rgba(elevated, 0.76), + "--bf-appearance-token-glass-border-base": rgba(accent, 0.12), + "--bf-appearance-token-glass-border-hover": rgba(accent, 0.22), + "--bf-appearance-token-color-error": colors["danger"], + "--bf-appearance-token-color-error-bg": rgba(colors["danger"], 0.28), + "--bf-appearance-token-color-error-border": rgba(colors["danger"], 0.38), + "--bf-appearance-token-color-warning": colors["warning"], + "--bf-appearance-token-color-success": colors["success"], + "--bf-appearance-token-color-info": colors["info"], + "--bf-appearance-token-scrollbar-thumb": rgba(accent, 0.22), + "--bf-appearance-token-scrollbar-thumb-hover": rgba(accent, 0.38), + }, + }, + }, + "monaco": { + "version": 1, + "settings": { + "id": appearance_id, + "base": "vs-dark", + "inherit": True, + "rules": [ + {"token": "comment", "foreground": colors["textDisabled"][1:], "fontStyle": "italic"}, + {"token": "keyword", "foreground": accent[1:]}, + {"token": "string", "foreground": colors["success"][1:]}, + {"token": "number", "foreground": colors["warning"][1:]}, + ], + "colors": { + "editor.background": background.upper(), + "editor.foreground": colors["text"].upper(), + "editorLineNumber.foreground": colors["textDisabled"].upper(), + "editorLineNumber.activeForeground": colors["textSecondary"].upper(), + "editor.selectionBackground": rgba(accent, 0.38), + "editor.inactiveSelectionBackground": rgba(accent, 0.24), + "editorCursor.foreground": accent.upper(), + "editorWidget.background": surface.upper(), + "editorHoverWidget.background": surface.upper(), + }, + }, + }, + "xterm": { + "version": 1, + "settings": { + "surfaces": { + name: { + "background": background, + "foreground": colors["text"], + "cursor": accent, + "selectionBackground": rgba(accent, 0.34), + } + for name in ("terminal", "output") + }, + "fontWeight": "normal", + "fontWeightBold": "700", + }, + }, + "mermaid": { + "version": 1, + "settings": { + "mode": "dark", + "palette": { + "nodeFill": elevated, + "nodeFillHover": surface, + "nodeText": colors["text"], + "nodeStroke": colors["accentStrong"], + "nodeStrokeHover": accent, + "clusterFill": background, + "clusterText": colors["textSecondary"], + "clusterStroke": colors["textDisabled"], + "edgeStroke": colors["accentStrong"], + "edgeLabelBackground": surface, + "edgeLabelText": colors["text"], + "noteFill": surface, + "noteText": colors["textSecondary"], + "noteStroke": colors["warning"], + "activationFill": elevated, + "activationStroke": accent, + "success": colors["success"], + "warning": colors["warning"], + "error": colors["danger"], + "errorBackground": surface, + "info": colors["info"], + "highlight": colors["accentStrong"], + "pieColors": [ + accent, + colors["danger"], + colors["warning"], + colors["success"], + colors["info"], + colors["accentStrong"], + colors["textMuted"], + colors["textSecondary"], + ], + }, + }, + }, + "generative-widget": { + "version": 1, + "settings": { + "id": appearance_id, + "mode": "dark", + "vars": { + "--bf-appearance-token-color-bg-primary": background, + "--bf-appearance-token-color-bg-secondary": surface, + "--bf-appearance-token-color-bg-tertiary": elevated, + "--bf-appearance-token-color-bg-elevated": elevated, + "--bf-appearance-token-color-bg-scene": background, + "--bf-appearance-token-color-text-primary": colors["text"], + "--bf-appearance-token-color-text-secondary": colors["textSecondary"], + "--bf-appearance-token-color-text-muted": colors["textMuted"], + "--bf-appearance-token-color-text-disabled": colors["textDisabled"], + "--bf-appearance-token-color-accent-500": accent, + "--bf-appearance-token-color-accent-500-rgb": rgb_triplet(accent, spaced=False), + "--bf-appearance-token-color-accent-600": colors["accentStrong"], + "--bf-appearance-token-border-subtle": rgba(accent, 0.08, spaced=False), + "--bf-appearance-token-border-base": rgba(accent, 0.12, spaced=False), + "--bf-appearance-token-border-medium": rgba(accent, 0.18, spaced=False), + "--bf-appearance-token-element-bg-subtle": rgba(surface, 0.50, spaced=False), + "--bf-appearance-token-element-bg-soft": rgba(surface, 0.58, spaced=False), + "--bf-appearance-token-element-bg-base": rgba(elevated, 0.66, spaced=False), + "--bf-appearance-token-element-bg-medium": rgba(elevated, 0.70, spaced=False), + "--bf-appearance-token-element-bg-hover": rgba(elevated, 0.78, spaced=False), + }, + }, + }, + "bitfun-canvas": { + "version": 1, + "settings": { + "id": appearance_id, + "mode": "dark", + "bg": background, + "panel": elevated, + "fg": colors["text"], + "muted": colors["textMuted"], + "border": colors["textDisabled"], + "accent": accent, + "success": colors["success"], + "warning": colors["warning"], + "danger": colors["danger"], + "info": colors["info"], + }, + }, + } + + +def build_assets() -> dict[str, Any]: + definitions = { + "background": ("video", "video/webm", "assets/background.webm"), + "floating": ("image", "image/webp", "assets/floating.webp"), + "sidebar": ("image", "image/webp", "assets/sidebar.webp"), + "card-detail": ("image", "image/webp", "assets/card-detail.webp"), + "card-portrait": ("image", "image/webp", "assets/card-portrait.webp"), + "dialog": ("image", "image/webp", "assets/dialog.webp"), + "preview": ("image", "image/webp", "assets/preview.webp"), + } + return { + asset_id: { + "kind": kind, + "mimeType": mime_type, + "source": {"kind": "package", "path": path}, + } + for asset_id, (kind, mime_type, path) in definitions.items() + } + + +def build_manifest( + *, + appearance_id: str, + name: str, + version: str, + mode: str, + palette: Mapping[str, Any], + surface_plan: Mapping[str, Any], + author: str | None = None, + description: str | None = None, +) -> dict[str, Any]: + colors = palette["colors"] + manifest: dict[str, Any] = { + "schema": "bitfun.appearance", + "schemaVersion": 1, + "id": appearance_id, + "name": name, + "version": version, + "mode": mode, + "preview": {"kind": "asset", "assetId": "preview"}, + "requiredCapabilities": ["assets.v1", "background-media.v1"], + "globals": {}, + "materials": build_materials(colors), + "components": resolve_palette_values(surface_plan["components"], colors), + "scenes": resolve_palette_values(surface_plan["scenes"], colors), + "renderers": build_renderers(colors, appearance_id), + "assets": build_assets(), + "integrity": {"sha256": {}}, + "backgroundMedia": { + "kind": "video", + "assetId": "background", + "posterAssetId": "preview", + "fit": "cover", + "position": "center", + }, + } + if author: + manifest["author"] = author + if description: + manifest["description"] = description + return manifest + + +def validate_manifest(manifest: Mapping[str, Any]) -> list[dict[str, str]]: + validator = appearance.ManifestValidator(appearance.load_registry()) + validator.validate(dict(manifest)) + if validator.errors: + formatted = appearance.format_issues(validator.errors) + raise CinematicContractError(f"Generated cinematic manifest is invalid:\n{formatted}") + return validator.warnings + + +def canonical_json(value: Any) -> str: + return json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True) + "\n" + + +def scaffold_comparable_manifest(value: Mapping[str, Any]) -> dict[str, Any]: + comparable = json.loads(json.dumps(value)) + comparable["integrity"] = {"sha256": {}} + return comparable diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/scaffold_cinematic.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/scaffold_cinematic.py new file mode 100644 index 0000000000..1529cbbaa5 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/examples/cinematic-animated-wallpaper/scripts/scaffold_cinematic.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + +SKILL_ROOT = Path(__file__).resolve().parents[3] +sys.path.insert(0, str(SKILL_ROOT / "scripts")) + +from build_support import atomic_write_json # noqa: E402 +from cinematic_recipe import ( # noqa: E402 + DEFAULT_PALETTE, + DEFAULT_SURFACE_PLAN, + CinematicContractError, + build_manifest, + canonical_json, + load_palette, + load_surface_plan, + scaffold_comparable_manifest, + validate_manifest, +) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Generate a cinematic BitFun Appearance manifest.") + parser.add_argument("--output", type=Path, required=True, help="Skin root containing package/.") + parser.add_argument("--id", required=True, dest="appearance_id") + parser.add_argument("--name", required=True) + parser.add_argument("--version", default="1.0.0") + parser.add_argument("--mode", choices=("light", "dark"), default="dark") + parser.add_argument("--author") + parser.add_argument("--description") + parser.add_argument("--palette", type=Path, default=DEFAULT_PALETTE) + parser.add_argument("--surface-plan", type=Path, default=DEFAULT_SURFACE_PLAN) + parser.add_argument("--force", action="store_true") + parser.add_argument("--check", action="store_true", help="Check that the existing manifest matches generated output.") + return parser.parse_args() + + +def generate(args: argparse.Namespace) -> tuple[Path, dict[str, object], list[dict[str, str]]]: + palette = load_palette(args.palette.resolve()) + surface_plan = load_surface_plan(args.surface_plan.resolve()) + manifest = build_manifest( + appearance_id=args.appearance_id, + name=args.name, + version=args.version, + mode=args.mode, + author=args.author, + description=args.description, + palette=palette, + surface_plan=surface_plan, + ) + warnings = validate_manifest(manifest) + manifest_path = args.output.resolve() / "package" / "appearance.json" + return manifest_path, manifest, warnings + + +def main() -> None: + args = parse_args() + try: + manifest_path, manifest, warnings = generate(args) + if args.check: + if not manifest_path.is_file(): + raise CinematicContractError(f"Manifest does not exist: {manifest_path}") + existing = json.loads(manifest_path.read_text(encoding="utf-8")) + if canonical_json(scaffold_comparable_manifest(existing)) != canonical_json( + scaffold_comparable_manifest(manifest) + ): + raise CinematicContractError(f"Manifest drift detected: {manifest_path}") + action = "checked" + else: + if manifest_path.exists() and not args.force: + raise CinematicContractError(f"Refusing to overwrite existing manifest: {manifest_path}") + atomic_write_json(manifest_path, manifest) + action = "written" + print(json.dumps({ + "manifest": str(manifest_path), + "action": action, + "components": len(manifest["components"]), + "scenes": len(manifest["scenes"]), + "warnings": warnings, + }, indent=2)) + except (CinematicContractError, OSError, json.JSONDecodeError) as error: + raise SystemExit(f"ERROR: {error}") from error + + +if __name__ == "__main__": + main() diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/appearance-registry.json b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/appearance-registry.json new file mode 100644 index 0000000000..35f2ff68ee --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/appearance-registry.json @@ -0,0 +1,17959 @@ +{ + "schema": "bitfun.appearance.registry", + "schemaVersion": 1, + "generatedFrom": "7fdf35b7be1c2693e33caae122dc970fb953865b", + "generatedAt": "2026-08-03T01:06:32.137402+00:00", + "sourceRevision": "7fdf35b7be1c2693e33caae122dc970fb953865b", + "sourceDirty": false, + "sourceTreeHash": "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b", + "components": [ + { + "id": "button", + "parts": [ + { + "id": "root", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "loadingIcon", + "propertyProfile": "control", + "visualRole": "decoration" + }, + { + "id": "loadingText", + "propertyProfile": "paint", + "visualRole": "content" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "primary", + "secondary", + "ghost", + "dashed", + "danger", + "success", + "accent", + "ai" + ] + }, + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + }, + { + "id": "iconOnly", + "attribute": "data-bf-icon-only", + "values": [ + "true" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover:not(:disabled)" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": ":active:not(:disabled)" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": ":disabled" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerContent", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "subtitle", + "propertyProfile": "container" + }, + { + "id": "extra", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "default", + "elevated", + "subtle", + "accent", + "purple" + ] + }, + { + "id": "padding", + "attribute": "data-bf-padding", + "values": [ + "none", + "small", + "medium", + "large" + ] + }, + { + "id": "radius", + "attribute": "data-bf-radius", + "values": [ + "small", + "medium", + "large" + ] + }, + { + "id": "align", + "attribute": "data-bf-align", + "values": [ + "left", + "center", + "right", + "between" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": ":active" + } + }, + { + "id": "interactive", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"interactive\"]" + } + }, + { + "id": "fullWidth", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"fullWidth\"]" + } + } + ] + }, + { + "id": "input", + "parts": [ + { + "id": "root", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "label", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "container", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "control", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "prefix", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "suffix", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "message", + "propertyProfile": "paint", + "visualRole": "content" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "default", + "filled", + "outlined" + ] + }, + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusWithin", + "selector": { + "kind": "self", + "suffix": ":focus-within" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "modal", + "parts": [ + { + "id": "overlay", + "propertyProfile": "overlay", + "visualRole": "popup" + }, + { + "id": "dialog", + "propertyProfile": "overlay", + "visualRole": "dialog" + }, + { + "id": "headerShell", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "modal-dialog" + }, + { + "id": "header", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "modal-dialog" + }, + { + "id": "titleGroup", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "title", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "titleExtra", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "close", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "content", + "propertyProfile": "container", + "visualRole": "content", + "continuityGroup": "modal-dialog" + }, + { + "id": "resizeHandle", + "propertyProfile": "overlay", + "visualRole": "control" + } + ], + "facets": [ + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large", + "xlarge" + ] + }, + { + "id": "placement", + "attribute": "data-bf-placement", + "values": [ + "center", + "bottom-left", + "bottom-right" + ] + }, + { + "id": "resizeDirection", + "attribute": "data-bf-resize-direction", + "values": [ + "n", + "s", + "w", + "e", + "nw", + "ne", + "sw", + "se" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + }, + { + "id": "draggable", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"draggable\"]" + } + }, + { + "id": "dragging", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dragging\"]" + } + }, + { + "id": "resizable", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"resizable\"]" + } + }, + { + "id": "resizing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"resizing\"]" + } + }, + { + "id": "contentInset", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"contentInset\"]" + } + } + ] + }, + { + "id": "tooltip", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "arrow", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "placement", + "attribute": "data-bf-placement", + "values": [ + "top", + "bottom", + "left", + "right" + ] + }, + { + "id": "interactive", + "attribute": "data-bf-interactive", + "values": [ + "true", + "false" + ] + } + ], + "states": [ + { + "id": "visible", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"visible\"]" + } + } + ] + }, + { + "id": "icon-button", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "default", + "primary", + "ghost", + "danger", + "success", + "warning", + "ai" + ] + }, + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "xs", + "small", + "medium", + "large" + ] + }, + { + "id": "shape", + "attribute": "data-bf-shape", + "values": [ + "square", + "circle" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover:not(:disabled)" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": ":active:not(:disabled)" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": ":disabled" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "checkbox", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "wrapper", + "propertyProfile": "container" + }, + { + "id": "input", + "propertyProfile": "container" + }, + { + "id": "box", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": ":hover" + } + }, + { + "id": "focusWithin", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": ":focus-within" + } + }, + { + "id": "checked", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": ":has(input:checked)" + } + }, + { + "id": "indeterminate", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"indeterminate\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "switch", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "wrapper", + "propertyProfile": "container" + }, + { + "id": "input", + "propertyProfile": "container" + }, + { + "id": "track", + "propertyProfile": "container" + }, + { + "id": "text", + "propertyProfile": "container" + }, + { + "id": "thumb", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusWithin", + "selector": { + "kind": "self", + "suffix": ":focus-within" + } + }, + { + "id": "checked", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"checked\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "textarea", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "required", + "propertyProfile": "container" + }, + { + "id": "wrapper", + "propertyProfile": "container" + }, + { + "id": "field", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + }, + { + "id": "count", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "default", + "filled", + "outlined" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusWithin", + "selector": { + "kind": "self", + "suffix": ":focus-within" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "autoResize", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"autoResize\"]" + } + } + ] + }, + { + "id": "number-input", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "container", + "propertyProfile": "container" + }, + { + "id": "glow", + "propertyProfile": "container" + }, + { + "id": "valueArea", + "propertyProfile": "container" + }, + { + "id": "input", + "propertyProfile": "container" + }, + { + "id": "unit", + "propertyProfile": "container" + }, + { + "id": "buttons", + "propertyProfile": "container" + }, + { + "id": "decrement", + "propertyProfile": "container" + }, + { + "id": "increment", + "propertyProfile": "container" + }, + { + "id": "progress", + "propertyProfile": "container" + }, + { + "id": "progressBar", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "default", + "compact", + "stepper" + ] + }, + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusWithin", + "selector": { + "kind": "self", + "suffix": ":focus-within" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "editing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"editing\"]" + } + }, + { + "id": "dragging", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dragging\"]" + } + } + ] + }, + { + "id": "search", + "parts": [ + { + "id": "root", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "wrapper", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "prefix", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "input", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "clear", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "suffix", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "button", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "message", + "propertyProfile": "paint", + "visualRole": "content" + } + ], + "facets": [ + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"hover\"]" + } + }, + { + "id": "focusWithin", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"focused\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "expandable", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expandable\"]" + } + } + ] + }, + { + "id": "tabs", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "nav", + "propertyProfile": "container" + }, + { + "id": "navList", + "propertyProfile": "container" + }, + { + "id": "tab", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "close", + "propertyProfile": "container" + }, + { + "id": "inkBar", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "pane", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "line", + "card", + "pill" + ] + }, + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "stretch", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"stretch\"]" + } + } + ] + }, + { + "id": "select", + "parts": [ + { + "id": "root", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "label", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "trigger", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "value", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "suffix", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "loading", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "clear", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "arrow", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "dropdown", + "propertyProfile": "overlay", + "visualRole": "popup" + }, + { + "id": "search", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "searchInput", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "options", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "option", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "optionIcon", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "optionLabel", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "optionDescription", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "group", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "groupLabel", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "empty", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "message", + "propertyProfile": "paint", + "visualRole": "content" + } + ], + "facets": [ + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + }, + { + "id": "placement", + "attribute": "data-bf-placement", + "values": [ + "top", + "bottom" + ] + }, + { + "id": "multiple", + "attribute": "data-bf-multiple", + "values": [ + "true", + "false" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusWithin", + "selector": { + "kind": "self", + "suffix": ":focus-within" + } + }, + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "highlighted", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"highlighted\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "alert", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "close", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "success", + "error", + "warning", + "info" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + } + ] + }, + { + "id": "badge", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "neutral", + "accent", + "purple", + "success", + "warning", + "error", + "info" + ] + } + ], + "states": [] + }, + { + "id": "tag", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "close", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "blue", + "green", + "red", + "yellow", + "purple", + "gray" + ] + }, + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + } + ], + "states": [ + { + "id": "rounded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"rounded\"]" + } + }, + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + } + ] + }, + { + "id": "avatar", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "image", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "text", + "propertyProfile": "container" + }, + { + "id": "group", + "propertyProfile": "container" + }, + { + "id": "overflow", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large", + "custom" + ] + }, + { + "id": "shape", + "attribute": "data-bf-shape", + "values": [ + "circle", + "square" + ] + } + ], + "states": [] + }, + { + "id": "code-editor", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "wrapper", + "propertyProfile": "container" + }, + { + "id": "fullscreenButton", + "propertyProfile": "container" + }, + { + "id": "editor", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "fullscreen", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"fullscreen\"]" + } + }, + { + "id": "readOnly", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"readOnly\"]" + } + } + ] + }, + { + "id": "config-page", + "parts": [ + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "confirm-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "type", + "attribute": "data-bf-type", + "values": [ + "info", + "warning", + "error", + "success" + ] + } + ], + "states": [] + }, + { + "id": "cube-loading", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "text", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + } + ], + "states": [] + }, + { + "id": "cube-logo", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "particles", + "propertyProfile": "container" + }, + { + "id": "particle", + "propertyProfile": "container" + }, + { + "id": "scene", + "propertyProfile": "container" + }, + { + "id": "cube", + "propertyProfile": "container" + }, + { + "id": "block", + "propertyProfile": "container" + }, + { + "id": "face", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "default", + "compact" + ] + } + ], + "states": [] + }, + { + "id": "dot-matrix-loader", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "dot", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "small", + "medium", + "large" + ] + } + ], + "states": [] + }, + { + "id": "empty", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "image", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "filter-pill", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "count", + "propertyProfile": "container" + }, + { + "id": "group", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + } + ] + }, + { + "id": "input-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "stream-text", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "character", + "propertyProfile": "container" + }, + { + "id": "cursor", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "effect", + "attribute": "data-bf-effect", + "values": [ + "smooth", + "typewriter", + "wave", + "fade", + "glitch", + "neon", + "matrix", + "gradient", + "pulse", + "blur", + "bounce", + "shimmer" + ] + }, + { + "id": "color", + "attribute": "data-bf-color", + "values": [ + "blue", + "purple", + "green", + "rainbow", + "fire", + "ocean", + "sunset" + ] + } + ], + "states": [ + { + "id": "streaming", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"streaming\"]" + } + }, + { + "id": "complete", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"complete\"]" + } + } + ] + }, + { + "id": "task-running-indicator", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "bar", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "size", + "attribute": "data-bf-size", + "values": [ + "xs", + "sm", + "md", + "lg" + ] + } + ], + "states": [] + }, + { + "id": "text-stroke-effect", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "outline", + "propertyProfile": "container" + }, + { + "id": "animated", + "propertyProfile": "container" + }, + { + "id": "gradient", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "window-controls", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "minimize", + "propertyProfile": "container" + }, + { + "id": "maximize", + "propertyProfile": "container" + }, + { + "id": "close", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "maximized", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"maximized\"]" + } + } + ] + }, + { + "id": "markdown", + "parts": [ + { + "id": "root", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "fallback", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "codeBlock", + "propertyProfile": "container", + "visualRole": "card" + }, + { + "id": "codeToolbar", + "propertyProfile": "control", + "visualRole": "toolbar" + }, + { + "id": "codeBody", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "codePre", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "codeContent", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "fileLink", + "propertyProfile": "container" + }, + { + "id": "visualizationLink", + "propertyProfile": "container" + }, + { + "id": "tabLink", + "propertyProfile": "container" + }, + { + "id": "table", + "propertyProfile": "container" + }, + { + "id": "blockquote", + "propertyProfile": "container" + }, + { + "id": "reproduction", + "propertyProfile": "container" + }, + { + "id": "reproductionHeader", + "propertyProfile": "container" + }, + { + "id": "reproductionContent", + "propertyProfile": "container" + }, + { + "id": "reproductionActions", + "propertyProfile": "container" + }, + { + "id": "math", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "fallback", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"fallback\"]" + } + }, + { + "id": "streaming", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"streaming\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "flow-chat-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "compact", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "result", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "details", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "processing", + "propertyProfile": "container" + }, + { + "id": "processingDot", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "display", + "attribute": "data-bf-display", + "values": [ + "compact", + "normal", + "detailed" + ] + }, + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "pending", + "preparing", + "running", + "streaming", + "completed", + "cancelled", + "error" + ] + } + ], + "states": [] + }, + { + "id": "chat-input", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "container", + "propertyProfile": "container" + }, + { + "id": "box", + "propertyProfile": "container" + }, + { + "id": "targetSwitcher", + "propertyProfile": "container" + }, + { + "id": "targetLabel", + "propertyProfile": "container" + }, + { + "id": "target", + "propertyProfile": "container" + }, + { + "id": "targetName", + "propertyProfile": "container" + }, + { + "id": "area", + "propertyProfile": "container" + }, + { + "id": "imageStrip", + "propertyProfile": "container" + }, + { + "id": "image", + "propertyProfile": "container" + }, + { + "id": "imagePreview", + "propertyProfile": "container" + }, + { + "id": "imageRemove", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + }, + { + "id": "commandPicker", + "propertyProfile": "container" + }, + { + "id": "commandHeader", + "propertyProfile": "container" + }, + { + "id": "commandList", + "propertyProfile": "container" + }, + { + "id": "commandItem", + "propertyProfile": "container" + }, + { + "id": "commandName", + "propertyProfile": "container" + }, + { + "id": "commandLabel", + "propertyProfile": "container" + }, + { + "id": "commandCurrent", + "propertyProfile": "container" + }, + { + "id": "commandSection", + "propertyProfile": "container" + }, + { + "id": "commandEmpty", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "actionsLeft", + "propertyProfile": "container" + }, + { + "id": "actionsRight", + "propertyProfile": "container" + }, + { + "id": "boost", + "propertyProfile": "container" + }, + { + "id": "boostTrigger", + "propertyProfile": "container" + }, + { + "id": "modeChip", + "propertyProfile": "container" + }, + { + "id": "modeChipLabel", + "propertyProfile": "container" + }, + { + "id": "modeChipRemove", + "propertyProfile": "container" + }, + { + "id": "boostMenu", + "propertyProfile": "container" + }, + { + "id": "boostSection", + "propertyProfile": "container" + }, + { + "id": "boostItem", + "propertyProfile": "container" + }, + { + "id": "boostItemLabel", + "propertyProfile": "container" + }, + { + "id": "boostItemMeta", + "propertyProfile": "container" + }, + { + "id": "boostDefaultAction", + "propertyProfile": "container" + }, + { + "id": "boostDivider", + "propertyProfile": "container" + }, + { + "id": "boostSubmenuTrigger", + "propertyProfile": "container" + }, + { + "id": "boostSubmenuPanel", + "propertyProfile": "container" + }, + { + "id": "boostSubmenuItem", + "propertyProfile": "container" + }, + { + "id": "boostSubmenuState", + "propertyProfile": "container" + }, + { + "id": "boostSubmenuManage", + "propertyProfile": "container" + }, + { + "id": "model", + "propertyProfile": "container" + }, + { + "id": "sendActions", + "propertyProfile": "container" + }, + { + "id": "sendButton", + "propertyProfile": "container" + }, + { + "id": "queuedBadge", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "target", + "attribute": "data-bf-target", + "values": [ + "main", + "btw" + ] + }, + { + "id": "command", + "attribute": "data-bf-command", + "values": [ + "actions", + "all", + "skills", + "modes" + ] + }, + { + "id": "commandItemKind", + "attribute": "data-bf-command-item-kind", + "values": [ + "action", + "mode", + "skill", + "mcp" + ] + }, + { + "id": "action", + "attribute": "data-bf-action", + "values": [ + "cancel", + "retry", + "send", + "split" + ] + }, + { + "id": "boostItemKind", + "attribute": "data-bf-boost-item-kind", + "values": [ + "mode", + "context", + "skill", + "manage" + ] + } + ], + "states": [ + { + "id": "processing", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"processing\"]" + } + }, + { + "id": "multiline", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"multiline\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "current", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"current\"]" + } + }, + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "pending", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"pending\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "composer-voice-input", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "control", + "propertyProfile": "container" + }, + { + "id": "pill", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "time", + "propertyProfile": "container" + }, + { + "id": "timeline", + "propertyProfile": "container" + }, + { + "id": "timelineBar", + "propertyProfile": "container" + }, + { + "id": "divider", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "phase", + "attribute": "data-bf-phase", + "values": [ + "idle", + "preparing", + "recording", + "transcribing" + ] + }, + { + "id": "action", + "attribute": "data-bf-action", + "values": [ + "cancel", + "transcribe", + "send" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "lowVolume", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"low-volume\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + } + ] + }, + { + "id": "session-usage-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "tabs", + "propertyProfile": "container" + }, + { + "id": "tab", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "metaRow", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "metric", + "propertyProfile": "container" + }, + { + "id": "table", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "tab", + "attribute": "data-bf-tab", + "values": [ + "overview", + "models", + "tools", + "files", + "errors", + "slowest" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "fallback", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"fallback\"]" + } + } + ] + }, + { + "id": "file-operation-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "previewText", + "propertyProfile": "container" + }, + { + "id": "guidance", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "path", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "action", + "attribute": "data-bf-action", + "values": [ + "create", + "modify", + "delete" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + } + ] + }, + { + "id": "rich-text-input", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "contextTag", + "propertyProfile": "container" + }, + { + "id": "tagBadge", + "propertyProfile": "container" + }, + { + "id": "tagText", + "propertyProfile": "container" + }, + { + "id": "tagRemove", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "contextType", + "attribute": "data-bf-context-type", + "values": [ + "file", + "directory", + "session-reference", + "code-snippet", + "pull-request", + "mermaid-node", + "mermaid-diagram", + "image", + "terminal-command", + "git-ref", + "url", + "web-element", + "widget-reference", + "skill-reference" + ] + } + ], + "states": [ + { + "id": "focused", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"focused\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + } + ] + }, + { + "id": "model-round-item", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "retryHistory", + "propertyProfile": "container" + }, + { + "id": "retryToggle", + "propertyProfile": "container" + }, + { + "id": "retryAttempt", + "propertyProfile": "container" + }, + { + "id": "attemptLabel", + "propertyProfile": "container" + }, + { + "id": "diagnosticToggle", + "propertyProfile": "container" + }, + { + "id": "diagnosticDetails", + "propertyProfile": "container" + }, + { + "id": "diagnosticSection", + "propertyProfile": "container" + }, + { + "id": "subagent", + "propertyProfile": "container" + }, + { + "id": "toolItem", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "metaItem", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "pending", + "queued", + "waiting", + "preparing", + "streaming", + "receiving", + "running", + "completed", + "error", + "cancelled", + "rejected", + "analyzing" + ] + } + ], + "states": [ + { + "id": "streaming", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"streaming\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "pending", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"pending\"]" + } + }, + { + "id": "copied", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"copied\"]" + } + } + ] + }, + { + "id": "deep-review-action-bar", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "progress", + "propertyProfile": "container" + }, + { + "id": "attribution", + "propertyProfile": "container" + }, + { + "id": "degradation", + "propertyProfile": "container" + }, + { + "id": "degradationOption", + "propertyProfile": "container" + }, + { + "id": "noIssues", + "propertyProfile": "container" + }, + { + "id": "fixDone", + "propertyProfile": "container" + }, + { + "id": "custom", + "propertyProfile": "container" + }, + { + "id": "customInput", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "phase", + "attribute": "data-bf-phase", + "values": [ + "review_running", + "review_completed", + "fix_running", + "fix_completed", + "fix_failed", + "fix_timeout", + "fix_interrupted", + "review_waiting_capacity", + "review_interrupted", + "resume_blocked", + "resume_running", + "resume_failed", + "review_error" + ] + }, + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "success", + "warning", + "error", + "info", + "loading" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "model-selector", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "trigger", + "propertyProfile": "container" + }, + { + "id": "name", + "propertyProfile": "container" + }, + { + "id": "contextUsage", + "propertyProfile": "container" + }, + { + "id": "dropdown", + "propertyProfile": "container" + }, + { + "id": "dropdownHeader", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "option", + "propertyProfile": "container" + }, + { + "id": "optionMain", + "propertyProfile": "container" + }, + { + "id": "configRow", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "flow-chat-header", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "leftActions", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + }, + { + "id": "turnBadge", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "backgroundActivity", + "propertyProfile": "container" + }, + { + "id": "activityPanel", + "propertyProfile": "container" + }, + { + "id": "activitySection", + "propertyProfile": "container" + }, + { + "id": "commandMenu", + "propertyProfile": "container" + }, + { + "id": "commandItem", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "searchControls", + "propertyProfile": "container" + }, + { + "id": "sessionTree", + "propertyProfile": "container" + }, + { + "id": "sessionTreeTrigger", + "propertyProfile": "container" + }, + { + "id": "sessionTreePanel", + "propertyProfile": "container" + }, + { + "id": "sessionTreeHeader", + "propertyProfile": "container" + }, + { + "id": "sessionTreeBody", + "propertyProfile": "container" + }, + { + "id": "sessionTreeNode", + "propertyProfile": "container" + }, + { + "id": "sessionTreeMenu", + "propertyProfile": "container" + }, + { + "id": "sessionTreeMenuItem", + "propertyProfile": "container" + }, + { + "id": "sessionTreeState", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "running", + "finishing", + "waiting", + "completed", + "cancelled", + "error", + "idle" + ] + } + ], + "states": [ + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "cancelling", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"cancelling\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "flow-chat-turn-rail", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "bar", + "propertyProfile": "container" + }, + { + "id": "tooltipContent", + "propertyProfile": "container" + }, + { + "id": "tooltipTurn", + "propertyProfile": "container" + }, + { + "id": "tooltipMessage", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "current", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"current\"]" + } + }, + { + "id": "visible", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"visible\"]" + } + } + ] + }, + { + "id": "session-files-badge", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "reviewMenu", + "propertyProfile": "container" + }, + { + "id": "reviewTrigger", + "propertyProfile": "container" + }, + { + "id": "reviewPopover", + "propertyProfile": "container" + }, + { + "id": "reviewItem", + "propertyProfile": "container" + }, + { + "id": "trigger", + "propertyProfile": "container" + }, + { + "id": "stats", + "propertyProfile": "container" + }, + { + "id": "popover", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "file", + "propertyProfile": "container" + }, + { + "id": "fileError", + "propertyProfile": "container" + }, + { + "id": "fileStats", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "operation", + "attribute": "data-bf-operation", + "values": [ + "create", + "modify", + "delete" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "code-review-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "details", + "propertyProfile": "container" + }, + { + "id": "reliability", + "propertyProfile": "container" + }, + { + "id": "reliabilityItem", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "issues", + "propertyProfile": "container" + }, + { + "id": "issue", + "propertyProfile": "container" + }, + { + "id": "remediation", + "propertyProfile": "container" + }, + { + "id": "groups", + "propertyProfile": "container" + }, + { + "id": "group", + "propertyProfile": "container" + }, + { + "id": "decision", + "propertyProfile": "container" + }, + { + "id": "remediationList", + "propertyProfile": "container" + }, + { + "id": "remediationItem", + "propertyProfile": "container" + }, + { + "id": "positive", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "create-agent-page", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "editorBar", + "propertyProfile": "container" + }, + { + "id": "back", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "heading", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "form", + "propertyProfile": "container" + }, + { + "id": "columns", + "propertyProfile": "container" + }, + { + "id": "column", + "propertyProfile": "container" + }, + { + "id": "field", + "propertyProfile": "container" + }, + { + "id": "levelGroup", + "propertyProfile": "container" + }, + { + "id": "levelOption", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "tools", + "propertyProfile": "container" + }, + { + "id": "tool", + "propertyProfile": "container" + }, + { + "id": "prompt", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + } + ] + }, + { + "id": "keyboard-shortcuts", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "key", + "propertyProfile": "container" + }, + { + "id": "keyBadge", + "propertyProfile": "container" + }, + { + "id": "revert", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "recording", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"recording\"]" + } + }, + { + "id": "conflict", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"conflict\"]" + } + }, + { + "id": "modified", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"modified\"]" + } + }, + { + "id": "readonly", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"readonly\"]" + } + } + ] + }, + { + "id": "task-tool-display", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "rail", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "interruption", + "propertyProfile": "container" + }, + { + "id": "reviewer", + "propertyProfile": "container" + }, + { + "id": "responsibilities", + "propertyProfile": "container" + }, + { + "id": "prompt", + "propertyProfile": "container" + }, + { + "id": "cancel", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "basics-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "launchAtLogin", + "propertyProfile": "container" + }, + { + "id": "autoUpdate", + "propertyProfile": "container" + }, + { + "id": "logging", + "propertyProfile": "container" + }, + { + "id": "logPath", + "propertyProfile": "container" + }, + { + "id": "terminal", + "propertyProfile": "container" + }, + { + "id": "shellOption", + "propertyProfile": "container" + }, + { + "id": "windowBehavior", + "propertyProfile": "container" + }, + { + "id": "notifications", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "markdown-editor", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "notice", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "modeToggle", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "view", + "attribute": "data-bf-view", + "values": [ + "preview", + "markdown", + "source" + ] + } + ], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "plan-viewer", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerMain", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "markdown", + "propertyProfile": "container" + }, + { + "id": "editorPanel", + "propertyProfile": "container" + }, + { + "id": "editor", + "propertyProfile": "container" + }, + { + "id": "todos", + "propertyProfile": "container" + }, + { + "id": "todo", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "app-layout", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "windowModeHint", + "propertyProfile": "container" + }, + { + "id": "windowModeTitle", + "propertyProfile": "container" + }, + { + "id": "windowModeDetail", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "fullscreen", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"fullscreen\"]" + } + }, + { + "id": "toolbar", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"toolbar\"]" + } + } + ] + }, + { + "id": "skill-group-picker", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "head", + "propertyProfile": "container" + }, + { + "id": "sections", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "group", + "propertyProfile": "container" + }, + { + "id": "groupHeader", + "propertyProfile": "container" + }, + { + "id": "groupActions", + "propertyProfile": "container" + }, + { + "id": "tokenGrid", + "propertyProfile": "container" + }, + { + "id": "token", + "propertyProfile": "container" + }, + { + "id": "manager", + "propertyProfile": "container" + }, + { + "id": "managerEditor", + "propertyProfile": "container" + }, + { + "id": "managerList", + "propertyProfile": "container" + }, + { + "id": "managerGroup", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "summaryGroup", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "working-copy-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + }, + { + "id": "commitBar", + "propertyProfile": "container" + }, + { + "id": "statusRow", + "propertyProfile": "container" + }, + { + "id": "branch", + "propertyProfile": "container" + }, + { + "id": "syncActions", + "propertyProfile": "container" + }, + { + "id": "commitInput", + "propertyProfile": "container" + }, + { + "id": "commitActions", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "fileList", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "groupHeader", + "propertyProfile": "container" + }, + { + "id": "file", + "propertyProfile": "container" + }, + { + "id": "fileCheck", + "propertyProfile": "container" + }, + { + "id": "fileName", + "propertyProfile": "container" + }, + { + "id": "fileStatus", + "propertyProfile": "container" + }, + { + "id": "resizer", + "propertyProfile": "container" + }, + { + "id": "diffArea", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "assistant-config-page", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "back", + "propertyProfile": "container" + }, + { + "id": "layout", + "propertyProfile": "container" + }, + { + "id": "identity", + "propertyProfile": "container" + }, + { + "id": "sessions", + "propertyProfile": "container" + }, + { + "id": "details", + "propertyProfile": "container" + }, + { + "id": "personaList", + "propertyProfile": "container" + }, + { + "id": "persona", + "propertyProfile": "container" + }, + { + "id": "editor", + "propertyProfile": "container" + }, + { + "id": "editorHeader", + "propertyProfile": "container" + }, + { + "id": "editorBody", + "propertyProfile": "container" + }, + { + "id": "frontmatter", + "propertyProfile": "container" + }, + { + "id": "bodyEditor", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "assistant-defaults-page", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "shell", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "zones", + "propertyProfile": "container" + }, + { + "id": "split", + "propertyProfile": "container" + }, + { + "id": "skillList", + "propertyProfile": "container" + }, + { + "id": "skill", + "propertyProfile": "container" + }, + { + "id": "toolList", + "propertyProfile": "container" + }, + { + "id": "tool", + "propertyProfile": "container" + }, + { + "id": "group", + "propertyProfile": "container" + }, + { + "id": "groupHeader", + "propertyProfile": "container" + }, + { + "id": "detail", + "propertyProfile": "container" + }, + { + "id": "detailHeader", + "propertyProfile": "container" + }, + { + "id": "detailBody", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "covered", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"covered\"]" + } + }, + { + "id": "collapsed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + } + ] + }, + { + "id": "task-detail-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "errorBanner", + "propertyProfile": "container" + }, + { + "id": "reviewer", + "propertyProfile": "container" + }, + { + "id": "prompt", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "execution", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "toolbar-mode", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerLeft", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "overflowTrigger", + "propertyProfile": "container" + }, + { + "id": "overflowMenu", + "propertyProfile": "container" + }, + { + "id": "overflowItem", + "propertyProfile": "container" + }, + { + "id": "collapsedActions", + "propertyProfile": "container" + }, + { + "id": "sessionSurface", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "stream", + "propertyProfile": "container" + }, + { + "id": "tool", + "propertyProfile": "container" + }, + { + "id": "toolName", + "propertyProfile": "container" + }, + { + "id": "toolSummary", + "propertyProfile": "container" + }, + { + "id": "todo", + "propertyProfile": "container" + }, + { + "id": "todoProgress", + "propertyProfile": "container" + }, + { + "id": "todoCurrent", + "propertyProfile": "container" + }, + { + "id": "streamText", + "propertyProfile": "container" + }, + { + "id": "controls", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "contentKind", + "attribute": "data-bf-content-kind", + "values": [ + "text", + "tool", + "todo" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "processing", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"processing\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "confirm", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"confirm\"]" + } + }, + { + "id": "streaming", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"streaming\"]" + } + }, + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + } + ] + }, + { + "id": "mcp-tool-display", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "info", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "text", + "propertyProfile": "container" + }, + { + "id": "image", + "propertyProfile": "container" + }, + { + "id": "resource", + "propertyProfile": "container" + }, + { + "id": "iframe", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "terminal-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "output", + "propertyProfile": "container" + }, + { + "id": "waiting", + "propertyProfile": "container" + }, + { + "id": "result", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "pending", + "queued", + "waiting", + "preparing", + "streaming", + "receiving", + "running", + "completed", + "error", + "cancelled", + "rejected", + "pending_confirmation", + "confirmed" + ] + } + ], + "states": [ + { + "id": "running", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"running\"]" + } + }, + { + "id": "completed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"completed\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "cancelled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"cancelled\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "skills-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "form", + "propertyProfile": "container" + }, + { + "id": "formBody", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "details", + "propertyProfile": "container" + }, + { + "id": "marketToolbar", + "propertyProfile": "container" + }, + { + "id": "marketList", + "propertyProfile": "container" + }, + { + "id": "marketItem", + "propertyProfile": "container" + }, + { + "id": "marketState", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "installed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"installed\"]" + } + }, + { + "id": "covered", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"covered\"]" + } + } + ] + }, + { + "id": "diff-editor", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "toolbarInfo", + "propertyProfile": "container" + }, + { + "id": "toolbarActions", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "agent-companion-desktop-pet", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "dock", + "propertyProfile": "container" + }, + { + "id": "bubbles", + "propertyProfile": "container" + }, + { + "id": "bubble", + "propertyProfile": "container" + }, + { + "id": "bubbleTitle", + "propertyProfile": "container" + }, + { + "id": "bubbleStatus", + "propertyProfile": "container" + }, + { + "id": "bubbleOutput", + "propertyProfile": "container" + }, + { + "id": "hitbox", + "propertyProfile": "container" + }, + { + "id": "pet", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "attention", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"attention\"]" + } + }, + { + "id": "typing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"typing\"]" + } + } + ] + }, + { + "id": "tool-group-picker", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "head", + "propertyProfile": "container" + }, + { + "id": "sections", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "group", + "propertyProfile": "container" + }, + { + "id": "groupHeader", + "propertyProfile": "container" + }, + { + "id": "groupActions", + "propertyProfile": "container" + }, + { + "id": "tokenGrid", + "propertyProfile": "container" + }, + { + "id": "token", + "propertyProfile": "container" + }, + { + "id": "manager", + "propertyProfile": "container" + }, + { + "id": "managerEditor", + "propertyProfile": "container" + }, + { + "id": "managerList", + "propertyProfile": "container" + }, + { + "id": "managerGroup", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "summaryGroup", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "inline-diff-preview", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + }, + { + "id": "notice", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "line", + "propertyProfile": "container" + }, + { + "id": "gutter", + "propertyProfile": "container" + }, + { + "id": "prefix", + "propertyProfile": "container" + }, + { + "id": "lineContent", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "export-image", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "logo", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "timestamp", + "propertyProfile": "container" + }, + { + "id": "user", + "propertyProfile": "container" + }, + { + "id": "assistant", + "propertyProfile": "container" + }, + { + "id": "round", + "propertyProfile": "container" + }, + { + "id": "text", + "propertyProfile": "container" + }, + { + "id": "thinking", + "propertyProfile": "container" + }, + { + "id": "tool", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "trigger", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "exporting", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"exporting\"]" + } + } + ] + }, + { + "id": "user-message-item", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "timestamp", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "steeringTag", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "images", + "propertyProfile": "container" + }, + { + "id": "image", + "propertyProfile": "container" + }, + { + "id": "blocks", + "propertyProfile": "container" + }, + { + "id": "lightbox", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "session-usage-report-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "metrics", + "propertyProfile": "container" + }, + { + "id": "metric", + "propertyProfile": "container" + }, + { + "id": "lists", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "listRow", + "propertyProfile": "container" + }, + { + "id": "fileStat", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "fallback", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "fallback", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"fallback\"]" + } + } + ] + }, + { + "id": "ask-user-question-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "questions", + "propertyProfile": "container" + }, + { + "id": "question", + "propertyProfile": "container" + }, + { + "id": "options", + "propertyProfile": "container" + }, + { + "id": "option", + "propertyProfile": "container" + }, + { + "id": "customInput", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "completed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"completed\"]" + } + } + ] + }, + { + "id": "create-plan-display", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerMain", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "overview", + "propertyProfile": "container" + }, + { + "id": "todos", + "propertyProfile": "container" + }, + { + "id": "todo", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "exec-process-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "output", + "propertyProfile": "container" + }, + { + "id": "result", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "waiting", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "cancelled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"cancelled\"]" + } + }, + { + "id": "waiting", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"waiting\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "editor-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "saving", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "saving", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"saving\"]" + } + } + ] + }, + { + "id": "appearance-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "settings", + "propertyProfile": "container" + }, + { + "id": "settingsContent", + "propertyProfile": "container" + }, + { + "id": "language", + "propertyProfile": "container" + }, + { + "id": "palettePicker", + "propertyProfile": "container" + }, + { + "id": "paletteSelect", + "propertyProfile": "container" + }, + { + "id": "paletteOption", + "propertyProfile": "container" + }, + { + "id": "packageSection", + "propertyProfile": "container" + }, + { + "id": "packageGrid", + "propertyProfile": "container" + }, + { + "id": "packageCard", + "propertyProfile": "container" + }, + { + "id": "packageSelect", + "propertyProfile": "container" + }, + { + "id": "packagePreview", + "propertyProfile": "container" + }, + { + "id": "packageCardBody", + "propertyProfile": "container" + }, + { + "id": "packageActions", + "propertyProfile": "container" + }, + { + "id": "packageActiveIndicator", + "propertyProfile": "container" + }, + { + "id": "packageEmpty", + "propertyProfile": "container" + }, + { + "id": "packageDiagnostics", + "propertyProfile": "container" + }, + { + "id": "packageDiagnosticsHeader", + "propertyProfile": "container" + }, + { + "id": "packageDiagnosticsGroup", + "propertyProfile": "container" + }, + { + "id": "packageDiagnosticIssue", + "propertyProfile": "container" + }, + { + "id": "packageDiagnosticAllowedParts", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "packageType", + "attribute": "data-bf-package-type", + "values": [ + "native", + "imported" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + } + ] + }, + { + "id": "lsp-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + }, + { + "id": "plugins", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "contentInner", + "propertyProfile": "container" + }, + { + "id": "sectionStack", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "sectionHeader", + "propertyProfile": "container" + }, + { + "id": "sectionTitle", + "propertyProfile": "container" + }, + { + "id": "sectionDescription", + "propertyProfile": "container" + }, + { + "id": "sectionBody", + "propertyProfile": "container" + }, + { + "id": "row", + "propertyProfile": "container" + }, + { + "id": "rowLabel", + "propertyProfile": "container" + }, + { + "id": "rowDescription", + "propertyProfile": "container" + }, + { + "id": "rowControl", + "propertyProfile": "container" + }, + { + "id": "pageHeader", + "propertyProfile": "container" + }, + { + "id": "collectionSection", + "propertyProfile": "container" + }, + { + "id": "collectionItem", + "propertyProfile": "container" + }, + { + "id": "collectionRow", + "propertyProfile": "container" + }, + { + "id": "collectionMeta", + "propertyProfile": "container" + }, + { + "id": "collectionName", + "propertyProfile": "container" + }, + { + "id": "collectionControl", + "propertyProfile": "container" + }, + { + "id": "collectionDetails", + "propertyProfile": "container" + }, + { + "id": "collectionToolbar", + "propertyProfile": "container" + }, + { + "id": "collectionEditor", + "propertyProfile": "container" + }, + { + "id": "collectionFilters", + "propertyProfile": "container" + }, + { + "id": "collectionList", + "propertyProfile": "container" + }, + { + "id": "subagentModel", + "propertyProfile": "container" + }, + { + "id": "modelOption", + "propertyProfile": "container" + }, + { + "id": "modelSelection", + "propertyProfile": "container" + }, + { + "id": "modelSelectionOption", + "propertyProfile": "container" + }, + { + "id": "modelSelectionInput", + "propertyProfile": "container" + }, + { + "id": "modelSelectionLabel", + "propertyProfile": "container" + }, + { + "id": "modelSelectionDropdown", + "propertyProfile": "container" + }, + { + "id": "pageHeaderInner", + "propertyProfile": "container" + }, + { + "id": "pageHeaderInfo", + "propertyProfile": "container" + }, + { + "id": "pageHeaderTitle", + "propertyProfile": "container" + }, + { + "id": "pageHeaderSubtitle", + "propertyProfile": "container" + }, + { + "id": "pageHeaderExtra", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "align", + "attribute": "data-bf-align", + "values": [ + "start", + "center" + ] + }, + { + "id": "layout", + "attribute": "data-bf-layout", + "values": [ + "default", + "multiline", + "wide", + "balanced" + ] + } + ], + "states": [] + }, + { + "id": "font-preference", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "levelGroup", + "propertyProfile": "container" + }, + { + "id": "levelButton", + "propertyProfile": "container" + }, + { + "id": "customControls", + "propertyProfile": "container" + }, + { + "id": "numberInput", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "flowChatControls", + "propertyProfile": "container" + }, + { + "id": "resetButton", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "level", + "attribute": "data-bf-level", + "values": [ + "compact", + "small", + "default", + "medium", + "large", + "custom" + ] + } + ], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "language-selector", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "trigger", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "code", + "propertyProfile": "container" + }, + { + "id": "menu", + "propertyProfile": "container" + }, + { + "id": "option", + "propertyProfile": "container" + }, + { + "id": "optionLabel", + "propertyProfile": "container" + }, + { + "id": "check", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "mode", + "attribute": "data-bf-mode", + "values": [ + "dropdown", + "inline", + "icon-only" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "changing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"changing\"]" + } + } + ] + }, + { + "id": "peer-device", + "parts": [ + { + "id": "overlay", + "propertyProfile": "container" + }, + { + "id": "dialog", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "closeButton", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "toolButton", + "propertyProfile": "container" + }, + { + "id": "path", + "propertyProfile": "container" + }, + { + "id": "pathInput", + "propertyProfile": "container" + }, + { + "id": "pathDisplay", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "selection", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "badge", + "propertyProfile": "container" + }, + { + "id": "badgeIcon", + "propertyProfile": "container" + }, + { + "id": "badgeLabel", + "propertyProfile": "container" + }, + { + "id": "disconnectButton", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": ":disabled" + } + } + ] + }, + { + "id": "update", + "parts": [ + { + "id": "availableRoot", + "propertyProfile": "container" + }, + { + "id": "lead", + "propertyProfile": "container" + }, + { + "id": "leadIcon", + "propertyProfile": "container" + }, + { + "id": "subtitle", + "propertyProfile": "container" + }, + { + "id": "versions", + "propertyProfile": "container" + }, + { + "id": "versionRow", + "propertyProfile": "container" + }, + { + "id": "versionLabel", + "propertyProfile": "container" + }, + { + "id": "versionValue", + "propertyProfile": "container" + }, + { + "id": "notes", + "propertyProfile": "container" + }, + { + "id": "notesLabel", + "propertyProfile": "container" + }, + { + "id": "notesBody", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "progressRoot", + "propertyProfile": "container" + }, + { + "id": "alert", + "propertyProfile": "container" + }, + { + "id": "progressBar", + "propertyProfile": "container" + }, + { + "id": "progressFill", + "propertyProfile": "container" + }, + { + "id": "progressHint", + "propertyProfile": "container" + }, + { + "id": "restartHint", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "daily", + "manual" + ] + }, + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "downloading", + "error", + "installed" + ] + } + ], + "states": [ + { + "id": "highlight", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"highlight\"]" + } + }, + { + "id": "indeterminate", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"indeterminate\"]" + } + } + ] + }, + { + "id": "component-preview", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "logo", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "appearanceSelector", + "propertyProfile": "container" + }, + { + "id": "container", + "propertyProfile": "container" + }, + { + "id": "sidebar", + "propertyProfile": "container" + }, + { + "id": "sidebarHeader", + "propertyProfile": "container" + }, + { + "id": "sidebarToggle", + "propertyProfile": "container" + }, + { + "id": "navigation", + "propertyProfile": "container" + }, + { + "id": "category", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "componentHeader", + "propertyProfile": "container" + }, + { + "id": "emptyState", + "propertyProfile": "container" + }, + { + "id": "markdownRoot", + "propertyProfile": "container" + }, + { + "id": "markdownHeader", + "propertyProfile": "container" + }, + { + "id": "markdownControls", + "propertyProfile": "container" + }, + { + "id": "markdownStage", + "propertyProfile": "container" + }, + { + "id": "markdownEditor", + "propertyProfile": "container" + }, + { + "id": "columnRoot", + "propertyProfile": "container" + }, + { + "id": "columnNavigation", + "propertyProfile": "container" + }, + { + "id": "columnItem", + "propertyProfile": "container" + }, + { + "id": "columnPreview", + "propertyProfile": "container" + }, + { + "id": "demoRoot", + "propertyProfile": "container" + }, + { + "id": "demoCard", + "propertyProfile": "container" + }, + { + "id": "demoHeader", + "propertyProfile": "container" + }, + { + "id": "demoStage", + "propertyProfile": "container" + }, + { + "id": "demoFooter", + "propertyProfile": "container" + }, + { + "id": "fullPageRoot", + "propertyProfile": "container" + }, + { + "id": "fullPageTabs", + "propertyProfile": "container" + }, + { + "id": "fullPageTab", + "propertyProfile": "container" + }, + { + "id": "fullPageContent", + "propertyProfile": "container" + }, + { + "id": "gridRoot", + "propertyProfile": "container" + }, + { + "id": "gridCard", + "propertyProfile": "container" + }, + { + "id": "gridHeader", + "propertyProfile": "container" + }, + { + "id": "gridPreview", + "propertyProfile": "container" + }, + { + "id": "gridCanvas", + "propertyProfile": "container" + }, + { + "id": "gridInfo", + "propertyProfile": "container" + }, + { + "id": "largeCardRoot", + "propertyProfile": "container" + }, + { + "id": "largeCard", + "propertyProfile": "container" + }, + { + "id": "largeCardHeader", + "propertyProfile": "container" + }, + { + "id": "largeCardPreview", + "propertyProfile": "container" + }, + { + "id": "expandButton", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "columns", + "attribute": "data-bf-columns", + "values": [ + "2", + "3", + "4" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "collapsed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "announcement", + "parts": [ + { + "id": "stack", + "propertyProfile": "container" + }, + { + "id": "deck", + "propertyProfile": "container" + }, + { + "id": "ghost", + "propertyProfile": "container" + }, + { + "id": "modalBackdrop", + "propertyProfile": "container" + }, + { + "id": "modal", + "propertyProfile": "container" + }, + { + "id": "modalClose", + "propertyProfile": "container" + }, + { + "id": "modalPages", + "propertyProfile": "container" + }, + { + "id": "modalFooter", + "propertyProfile": "container" + }, + { + "id": "modalNavigation", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "depth", + "attribute": "data-bf-depth", + "values": [ + "1", + "2" + ] + } + ], + "states": [] + }, + { + "id": "context-menu", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "separator", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "shortcut", + "propertyProfile": "container" + }, + { + "id": "submenuArrow", + "propertyProfile": "container" + }, + { + "id": "submenu", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "submenuActive", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"submenu-active\"]" + } + } + ] + }, + { + "id": "context-list", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "count", + "propertyProfile": "container" + }, + { + "id": "clear", + "propertyProfile": "container" + }, + { + "id": "items", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "card", + "propertyProfile": "container" + }, + { + "id": "cardIndicator", + "propertyProfile": "container" + }, + { + "id": "cardBody", + "propertyProfile": "container" + }, + { + "id": "cardToolbar", + "propertyProfile": "container" + }, + { + "id": "cardError", + "propertyProfile": "container" + }, + { + "id": "dropZone", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + }, + { + "id": "valid", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"valid\"]" + } + }, + { + "id": "invalid", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"invalid\"]" + } + }, + { + "id": "dragOver", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"drag-over\"]" + } + }, + { + "id": "canAccept", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"can-accept\"]" + } + } + ] + }, + { + "id": "notification", + "parts": [ + { + "id": "container", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemIcon", + "propertyProfile": "container" + }, + { + "id": "itemContent", + "propertyProfile": "container" + }, + { + "id": "itemTitle", + "propertyProfile": "container" + }, + { + "id": "itemMessage", + "propertyProfile": "container" + }, + { + "id": "itemActions", + "propertyProfile": "container" + }, + { + "id": "itemClose", + "propertyProfile": "container" + }, + { + "id": "progressItem", + "propertyProfile": "container" + }, + { + "id": "progressBar", + "propertyProfile": "container" + }, + { + "id": "progressFill", + "propertyProfile": "container" + }, + { + "id": "loadingItem", + "propertyProfile": "container" + }, + { + "id": "centerRoot", + "propertyProfile": "container" + }, + { + "id": "centerHeader", + "propertyProfile": "container" + }, + { + "id": "centerList", + "propertyProfile": "container" + }, + { + "id": "centerItem", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "success", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"success\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "unread", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"unread\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "canvas-tool", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + }, + { + "id": "diagnostics", + "propertyProfile": "container" + }, + { + "id": "source", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "frame", + "propertyProfile": "container" + }, + { + "id": "sourceOverlay", + "propertyProfile": "container" + }, + { + "id": "sourceDialog", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "generative-widget", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "toolbarMeta", + "propertyProfile": "container" + }, + { + "id": "toolbarActions", + "propertyProfile": "container" + }, + { + "id": "workspace", + "propertyProfile": "container" + }, + { + "id": "editorPane", + "propertyProfile": "container" + }, + { + "id": "paneHeader", + "propertyProfile": "container" + }, + { + "id": "editor", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "frame", + "propertyProfile": "container" + }, + { + "id": "iframe", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "editor-tool", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "saving", + "propertyProfile": "container" + }, + { + "id": "readOnlyCodeBlock", + "propertyProfile": "container" + }, + { + "id": "meditorPreview", + "propertyProfile": "container" + }, + { + "id": "meditorEditArea", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "largeFile", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"large-file\"]" + } + }, + { + "id": "fullscreen", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"fullscreen\"]" + } + } + ] + }, + { + "id": "file-system", + "parts": [ + { + "id": "searchResults", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "match", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "git-tool", + "parts": [ + { + "id": "graphRoot", + "propertyProfile": "container" + }, + { + "id": "graphHeader", + "propertyProfile": "container" + }, + { + "id": "graphContent", + "propertyProfile": "container" + }, + { + "id": "graphCanvas", + "propertyProfile": "container" + }, + { + "id": "graphStatus", + "propertyProfile": "container" + }, + { + "id": "pushButton", + "propertyProfile": "container" + }, + { + "id": "diffEditor", + "propertyProfile": "container" + }, + { + "id": "createBranchDialog", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "lsp", + "parts": [ + { + "id": "pluginList", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "items", + "propertyProfile": "container" + }, + { + "id": "plugin", + "propertyProfile": "container" + }, + { + "id": "pluginHeader", + "propertyProfile": "container" + }, + { + "id": "pluginDetails", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "terminal-tool", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "terminal-tool" + }, + { + "id": "loading", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "error", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "toolbar", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "terminal-tool" + }, + { + "id": "statusBar", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "terminal-tool" + }, + { + "id": "terminal", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "terminal-tool" + }, + { + "id": "output", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "terminal-tool" + }, + { + "id": "screen", + "propertyProfile": "layout", + "visualRole": "content" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "running", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"running\"]" + } + }, + { + "id": "exited", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"exited\"]" + } + } + ] + }, + { + "id": "workspace-tool", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "currentSection", + "propertyProfile": "container" + }, + { + "id": "recentSection", + "propertyProfile": "container" + }, + { + "id": "currentCard", + "propertyProfile": "container" + }, + { + "id": "recentCard", + "propertyProfile": "container" + }, + { + "id": "assistantCard", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "git-graph-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "nav-bar", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "panelToggle", + "propertyProfile": "container" + }, + { + "id": "back", + "propertyProfile": "container" + }, + { + "id": "forward", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "collapsed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + } + ] + }, + { + "id": "splash-screen", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "center", + "propertyProfile": "container" + }, + { + "id": "logo", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "exiting", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"exiting\"]" + } + } + ] + }, + { + "id": "chat-pane", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "session-workspace" + } + ], + "facets": [], + "states": [] + }, + { + "id": "aux-pane", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "bottom-terminal-pane", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "scheduled-jobs-modal", + "parts": [ + { + "id": "body", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "scene-bar", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "tabs", + "propertyProfile": "container" + }, + { + "id": "controls", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "panel-header", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "terminal-edit-modal", + "parts": [ + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "gallery-layout", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "skill-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "iconArea", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "badges", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "titleRow", + "propertyProfile": "container" + }, + { + "id": "name", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "skill", + "market" + ] + }, + { + "id": "tone", + "attribute": "data-bf-tone", + "values": [ + "primary", + "danger", + "success", + "muted" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + } + ] + }, + { + "id": "mini-app-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "iconArea", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "name", + "propertyProfile": "container" + }, + { + "id": "version", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "tags", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "agent-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "iconArea", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "headerInfo", + "propertyProfile": "container" + }, + { + "id": "titleRow", + "propertyProfile": "container" + }, + { + "id": "name", + "propertyProfile": "container" + }, + { + "id": "badges", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "capabilities", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "core-agent-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "headerInfo", + "propertyProfile": "container" + }, + { + "id": "name", + "propertyProfile": "container" + }, + { + "id": "role", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "tags", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "agent-capability-tooltip", + "parts": [ + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "fields", + "propertyProfile": "container" + }, + { + "id": "field", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "git-nav", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "sections", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "file-viewer-nav", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "assistant-quick-input", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "box", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "footerLeft", + "propertyProfile": "container" + }, + { + "id": "hint", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "gallery-detail-modal", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "hero", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "badges", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "mcp-interaction-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "params", + "propertyProfile": "container" + }, + { + "id": "editor", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "remote-connect-disclaimer", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "intro", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "riskList", + "propertyProfile": "container" + }, + { + "id": "details", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "diff-fullscreen-viewer", + "parts": [ + { + "id": "overlay", + "propertyProfile": "container" + }, + { + "id": "container", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "fileInfo", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "notification-button", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "progress", + "propertyProfile": "container" + }, + { + "id": "loadingIcon", + "propertyProfile": "container" + }, + { + "id": "progressIcon", + "propertyProfile": "container" + }, + { + "id": "progressText", + "propertyProfile": "container" + }, + { + "id": "tooltip", + "propertyProfile": "container" + }, + { + "id": "tooltipContent", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "context-compression-card", + "parts": [ + { + "id": "compact", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "tokens", + "propertyProfile": "container" + }, + { + "id": "result", + "propertyProfile": "container" + }, + { + "id": "processing", + "propertyProfile": "container" + }, + { + "id": "summaryRow", + "propertyProfile": "container" + }, + { + "id": "statsRow", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "display", + "attribute": "data-bf-display", + "values": [ + "compact" + ] + }, + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "pending", + "preparing", + "running", + "streaming", + "completed", + "cancelled", + "error" + ] + } + ], + "states": [] + }, + { + "id": "review-session-summary-card", + "parts": [ + { + "id": "fileCount", + "propertyProfile": "container" + }, + { + "id": "details", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "files", + "propertyProfile": "container" + }, + { + "id": "open", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "relay-deploy", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "steps", + "propertyProfile": "container" + }, + { + "id": "step", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "completed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"completed\"]" + } + } + ] + }, + { + "id": "ssh-remote", + "parts": [ + { + "id": "connection", + "propertyProfile": "container" + }, + { + "id": "connectionError", + "propertyProfile": "container" + }, + { + "id": "connectionContent", + "propertyProfile": "container" + }, + { + "id": "connectionSection", + "propertyProfile": "container" + }, + { + "id": "connectionList", + "propertyProfile": "container" + }, + { + "id": "connectionItem", + "propertyProfile": "container" + }, + { + "id": "connectionForm", + "propertyProfile": "container" + }, + { + "id": "connectionActions", + "propertyProfile": "container" + }, + { + "id": "browserOverlay", + "propertyProfile": "container" + }, + { + "id": "browser", + "propertyProfile": "container" + }, + { + "id": "browserHeader", + "propertyProfile": "container" + }, + { + "id": "breadcrumb", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "authDialog", + "propertyProfile": "container" + }, + { + "id": "authDescription", + "propertyProfile": "container" + }, + { + "id": "authField", + "propertyProfile": "container" + }, + { + "id": "authActions", + "propertyProfile": "container" + }, + { + "id": "confirmDialog", + "propertyProfile": "container" + }, + { + "id": "confirmWarning", + "propertyProfile": "container" + }, + { + "id": "confirmMessage", + "propertyProfile": "container" + }, + { + "id": "confirmActions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "about-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container", + "visualRole": "dialog", + "continuityGroup": "about-dialog" + }, + { + "id": "hero", + "propertyProfile": "container", + "visualRole": "content", + "continuityGroup": "about-dialog" + }, + { + "id": "title", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "version", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "decoration", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "content", + "propertyProfile": "container", + "visualRole": "content", + "continuityGroup": "about-dialog" + }, + { + "id": "updateCard", + "propertyProfile": "container", + "visualRole": "card" + }, + { + "id": "updateHeader", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "updateFeedback", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "updateActions", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "progress", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "progressFill", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "infoCard", + "propertyProfile": "container", + "visualRole": "card" + }, + { + "id": "infoRow", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "infoLabel", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "infoValue", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "copyButton", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "footer", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "about-dialog" + }, + { + "id": "license", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "copyright", + "propertyProfile": "paint", + "visualRole": "content" + } + ], + "facets": [], + "states": [ + { + "id": "checking", + "selector": { + "kind": "ancestorPart", + "part": "updateCard", + "suffix": "[data-bf-state~=\"checking\"]" + } + }, + { + "id": "latest", + "selector": { + "kind": "ancestorPart", + "part": "updateCard", + "suffix": "[data-bf-state~=\"latest\"]" + } + }, + { + "id": "downloading", + "selector": { + "kind": "ancestorPart", + "part": "updateCard", + "suffix": "[data-bf-state~=\"downloading\"]" + } + }, + { + "id": "installed", + "selector": { + "kind": "ancestorPart", + "part": "updateCard", + "suffix": "[data-bf-state~=\"installed\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "ancestorPart", + "part": "updateCard", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "nav-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "panel", + "continuityGroup": "nav-panel" + }, + { + "id": "content", + "propertyProfile": "container", + "visualRole": "continuous-surface", + "continuityGroup": "nav-panel" + }, + { + "id": "mainLayer", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "nav-panel" + }, + { + "id": "sceneLayer", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "nav-panel" + }, + { + "id": "sceneContent", + "propertyProfile": "container", + "visualRole": "content", + "continuityGroup": "nav-panel" + }, + { + "id": "brandHeader", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "nav-panel" + }, + { + "id": "search", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "searchTrigger", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "topActions", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "topAction", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "extensionGroup", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "sections", + "propertyProfile": "container", + "visualRole": "content", + "continuityGroup": "nav-panel" + }, + { + "id": "section", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "sectionContent", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "bottomBar", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "nav-panel" + }, + { + "id": "miniAppFooter", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "nav-panel" + }, + { + "id": "footer", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "nav-panel" + }, + { + "id": "footerButton", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "footerMenu", + "propertyProfile": "overlay", + "visualRole": "popup" + }, + { + "id": "footerMenuItem", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "footerMenuDivider", + "propertyProfile": "paint", + "visualRole": "divider" + }, + { + "id": "workspaceMenu", + "propertyProfile": "overlay", + "visualRole": "popup" + }, + { + "id": "workspaceMenuItem", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "workspaceMenuDivider", + "propertyProfile": "paint", + "visualRole": "divider" + }, + { + "id": "workspaceMenuTitle", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "workspaceMenuEmpty", + "propertyProfile": "container", + "visualRole": "content" + } + ], + "facets": [ + { + "id": "layer", + "attribute": "data-bf-layer", + "values": [ + "main", + "scene" + ] + }, + { + "id": "action", + "attribute": "data-bf-action", + "values": [ + "code", + "cowork", + "assistant", + "extensions", + "agents", + "skills" + ] + }, + { + "id": "section", + "attribute": "data-bf-section", + "values": [ + "assistant-sessions", + "workspace" + ] + } + ], + "states": [ + { + "id": "scene", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"scene\"]" + } + }, + { + "id": "split", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"split\"]" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "closing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"closing\"]" + } + }, + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "sessions-section", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "retry", + "propertyProfile": "container" + }, + { + "id": "row", + "propertyProfile": "container" + }, + { + "id": "rowMain", + "propertyProfile": "container" + }, + { + "id": "edit", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "menu", + "propertyProfile": "container" + }, + { + "id": "toggle", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "editing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"editing\"]" + } + }, + { + "id": "menuOpen", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"menuOpen\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "content-canvas", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "workspace", + "continuityGroup": "content-canvas" + }, + { + "id": "main", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "content-canvas" + }, + { + "id": "editor", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "content-canvas" + }, + { + "id": "anchorContent", + "propertyProfile": "container", + "visualRole": "content", + "continuityGroup": "content-canvas-anchor" + }, + { + "id": "anchor", + "propertyProfile": "layout", + "visualRole": "panel" + }, + { + "id": "anchorResizer", + "propertyProfile": "container" + }, + { + "id": "anchorHeader", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "content-canvas-anchor" + }, + { + "id": "anchorTitle", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "anchorActions", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "splitHandle", + "propertyProfile": "control", + "visualRole": "divider" + }, + { + "id": "dropZone", + "propertyProfile": "overlay", + "visualRole": "decoration" + }, + { + "id": "dropContent", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "dropOverlay", + "propertyProfile": "overlay", + "visualRole": "decoration" + }, + { + "id": "dropTarget", + "propertyProfile": "overlay", + "visualRole": "decoration" + }, + { + "id": "dropIndicator", + "propertyProfile": "overlay", + "visualRole": "divider" + }, + { + "id": "empty", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "emptyToolbar", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "emptyContent", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "quickLook", + "propertyProfile": "overlay", + "visualRole": "popup" + }, + { + "id": "quickLookHeader", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "content-canvas-quick-look" + }, + { + "id": "quickLookTitle", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "quickLookActions", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "quickLookContent", + "propertyProfile": "container", + "visualRole": "content", + "continuityGroup": "content-canvas-quick-look" + }, + { + "id": "quickLookFooter", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "content-canvas-quick-look" + }, + { + "id": "searchFilter", + "propertyProfile": "control", + "visualRole": "control" + } + ], + "facets": [ + { + "id": "mode", + "attribute": "data-bf-mode", + "values": [ + "agent", + "project", + "git", + "bottom-terminal" + ] + }, + { + "id": "direction", + "attribute": "data-bf-direction", + "values": [ + "horizontal", + "vertical" + ] + }, + { + "id": "position", + "attribute": "data-bf-position", + "values": [ + "left", + "right", + "top", + "bottom", + "center" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "maximized", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"maximized\"]" + } + }, + { + "id": "resizing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"resizing\"]" + } + }, + { + "id": "collapsed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + }, + { + "id": "dragging", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dragging\"]" + } + }, + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "files-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "searchToolbar", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "transfers", + "propertyProfile": "container" + }, + { + "id": "transfer", + "propertyProfile": "container" + }, + { + "id": "transferTrack", + "propertyProfile": "container" + }, + { + "id": "transferFill", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "searchMode", + "attribute": "data-bf-search-mode", + "values": [ + "content", + "filenames" + ] + } + ], + "states": [] + }, + { + "id": "review-platform", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "chrome", + "propertyProfile": "container" + }, + { + "id": "brand", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "subtitle", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "statusBar", + "propertyProfile": "container" + }, + { + "id": "statusLine", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "listPane", + "propertyProfile": "container" + }, + { + "id": "listToolbar", + "propertyProfile": "container" + }, + { + "id": "filters", + "propertyProfile": "container" + }, + { + "id": "listScroll", + "propertyProfile": "container" + }, + { + "id": "listItem", + "propertyProfile": "container" + }, + { + "id": "listItemMain", + "propertyProfile": "container" + }, + { + "id": "listItemTitle", + "propertyProfile": "container" + }, + { + "id": "listItemMeta", + "propertyProfile": "container" + }, + { + "id": "listItemStats", + "propertyProfile": "container" + }, + { + "id": "pagination", + "propertyProfile": "container" + }, + { + "id": "emptyState", + "propertyProfile": "container" + }, + { + "id": "errorState", + "propertyProfile": "container" + }, + { + "id": "loadingState", + "propertyProfile": "container" + }, + { + "id": "authGate", + "propertyProfile": "container" + }, + { + "id": "authCopy", + "propertyProfile": "container" + }, + { + "id": "authActions", + "propertyProfile": "container" + }, + { + "id": "detailPane", + "propertyProfile": "container" + }, + { + "id": "detailHeader", + "propertyProfile": "container" + }, + { + "id": "detailTitle", + "propertyProfile": "container" + }, + { + "id": "detailState", + "propertyProfile": "container" + }, + { + "id": "detailMeta", + "propertyProfile": "container" + }, + { + "id": "detailActions", + "propertyProfile": "container" + }, + { + "id": "facts", + "propertyProfile": "container" + }, + { + "id": "tabContent", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "sectionHeading", + "propertyProfile": "container" + }, + { + "id": "sectionActions", + "propertyProfile": "container" + }, + { + "id": "ciItem", + "propertyProfile": "container" + }, + { + "id": "ciHead", + "propertyProfile": "container" + }, + { + "id": "ciStatus", + "propertyProfile": "container" + }, + { + "id": "ciLog", + "propertyProfile": "container" + }, + { + "id": "thread", + "propertyProfile": "container" + }, + { + "id": "threadHead", + "propertyProfile": "container" + }, + { + "id": "threadBody", + "propertyProfile": "container" + }, + { + "id": "fileList", + "propertyProfile": "container" + }, + { + "id": "fileCard", + "propertyProfile": "container" + }, + { + "id": "fileRow", + "propertyProfile": "container" + }, + { + "id": "fileMain", + "propertyProfile": "container" + }, + { + "id": "fileStatus", + "propertyProfile": "container" + }, + { + "id": "filePath", + "propertyProfile": "container" + }, + { + "id": "fileDelta", + "propertyProfile": "container" + }, + { + "id": "diff", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "layout", + "attribute": "data-bf-layout", + "values": [ + "full", + "detail" + ] + } + ], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "remote-connect-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "groups", + "propertyProfile": "container" + }, + { + "id": "groupTab", + "propertyProfile": "container" + }, + { + "id": "subtabs", + "propertyProfile": "container" + }, + { + "id": "subtab", + "propertyProfile": "container" + }, + { + "id": "panel", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "group", + "attribute": "data-bf-group", + "values": [ + "network", + "bot", + "account" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "connected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"connected\"]" + } + } + ] + }, + { + "id": "remote-account-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "scroll", + "propertyProfile": "container" + }, + { + "id": "form", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "syncOptions", + "propertyProfile": "container" + }, + { + "id": "syncOption", + "propertyProfile": "container" + }, + { + "id": "server", + "propertyProfile": "container" + }, + { + "id": "syncStatus", + "propertyProfile": "container" + }, + { + "id": "progressTrack", + "propertyProfile": "container" + }, + { + "id": "progressFill", + "propertyProfile": "container" + }, + { + "id": "deviceList", + "propertyProfile": "container" + }, + { + "id": "deviceCard", + "propertyProfile": "container" + }, + { + "id": "pages", + "propertyProfile": "container" + }, + { + "id": "pagesEntry", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "view", + "attribute": "data-bf-view", + "values": [ + "login", + "overwrite", + "devices" + ] + } + ], + "states": [ + { + "id": "offline", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"offline\"]" + } + }, + { + "id": "current", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"current\"]" + } + }, + { + "id": "syncing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"syncing\"]" + } + } + ] + }, + { + "id": "scheduled-jobs-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "target", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "job", + "propertyProfile": "container" + }, + { + "id": "jobBody", + "propertyProfile": "container" + }, + { + "id": "jobActions", + "propertyProfile": "container" + }, + { + "id": "jobMeta", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "editor", + "propertyProfile": "container" + }, + { + "id": "form", + "propertyProfile": "container" + }, + { + "id": "warning", + "propertyProfile": "container" + }, + { + "id": "field", + "propertyProfile": "container" + }, + { + "id": "fieldMeta", + "propertyProfile": "container" + }, + { + "id": "fieldControl", + "propertyProfile": "container" + }, + { + "id": "toggle", + "propertyProfile": "container" + }, + { + "id": "formActions", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "target", + "attribute": "data-bf-target", + "values": [ + "session", + "workspace" + ] + }, + { + "id": "schedule", + "attribute": "data-bf-schedule", + "values": [ + "at", + "every", + "cron" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "flexible-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerMain", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "code", + "propertyProfile": "container" + }, + { + "id": "markdown", + "propertyProfile": "container" + }, + { + "id": "markdownEditor", + "propertyProfile": "container" + }, + { + "id": "text", + "propertyProfile": "container" + }, + { + "id": "viewer", + "propertyProfile": "container" + }, + { + "id": "image", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "aiSession", + "propertyProfile": "container" + }, + { + "id": "sessionHeader", + "propertyProfile": "container" + }, + { + "id": "sessionDetails", + "propertyProfile": "container" + }, + { + "id": "operations", + "propertyProfile": "container" + }, + { + "id": "operation", + "propertyProfile": "container" + }, + { + "id": "terminal", + "propertyProfile": "container" + }, + { + "id": "unknown", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "needsFix", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"needsFix\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "btw-session-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerMain", + "propertyProfile": "container" + }, + { + "id": "badge", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "scrollToBottom", + "propertyProfile": "container" + }, + { + "id": "minimized", + "propertyProfile": "container" + }, + { + "id": "actionBar", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "view", + "attribute": "data-bf-view", + "values": [ + "empty", + "session" + ] + } + ], + "states": [ + { + "id": "hasActionBar", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"hasActionBar\"]" + } + }, + { + "id": "minimized", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"minimized\"]" + } + } + ] + }, + { + "id": "modern-flow-chat", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "messages", + "propertyProfile": "container" + }, + { + "id": "historyOverlay", + "propertyProfile": "container" + }, + { + "id": "historyOpenIntent", + "propertyProfile": "container" + }, + { + "id": "historyOpenIntentSpinner", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "virtual-message-list", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "emptyMessage", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "boundaryStatus", + "propertyProfile": "container" + }, + { + "id": "items", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "handoffOverlay", + "propertyProfile": "container" + }, + { + "id": "handoffContent", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + }, + { + "id": "preparing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"preparing\"]" + } + }, + { + "id": "unavailable", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"unavailable\"]" + } + } + ] + }, + { + "id": "ai-model-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "providerSelection", + "propertyProfile": "container" + }, + { + "id": "customOption", + "propertyProfile": "container" + }, + { + "id": "customOptionTitle", + "propertyProfile": "container" + }, + { + "id": "customOptionDescription", + "propertyProfile": "container" + }, + { + "id": "selectionDivider", + "propertyProfile": "container" + }, + { + "id": "providerGrid", + "propertyProfile": "container" + }, + { + "id": "providerCard", + "propertyProfile": "container" + }, + { + "id": "providerName", + "propertyProfile": "container" + }, + { + "id": "providerDescription", + "propertyProfile": "container" + }, + { + "id": "providerModels", + "propertyProfile": "container" + }, + { + "id": "providerTag", + "propertyProfile": "container" + }, + { + "id": "selectionActions", + "propertyProfile": "container" + }, + { + "id": "form", + "propertyProfile": "container" + }, + { + "id": "formBody", + "propertyProfile": "container" + }, + { + "id": "formActions", + "propertyProfile": "container" + }, + { + "id": "subscriptionArea", + "propertyProfile": "container" + }, + { + "id": "subscriptionNotice", + "propertyProfile": "container" + }, + { + "id": "subscriptionPanel", + "propertyProfile": "container" + }, + { + "id": "subscriptionSummary", + "propertyProfile": "container" + }, + { + "id": "subscriptionCode", + "propertyProfile": "container" + }, + { + "id": "subscriptionActions", + "propertyProfile": "container" + }, + { + "id": "collection", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "providerGroup", + "propertyProfile": "container" + }, + { + "id": "providerGroupHeader", + "propertyProfile": "container" + }, + { + "id": "providerGroupTitle", + "propertyProfile": "container" + }, + { + "id": "providerGroupActions", + "propertyProfile": "container" + }, + { + "id": "providerGroupList", + "propertyProfile": "container" + }, + { + "id": "modelItem", + "propertyProfile": "container" + }, + { + "id": "modelMeta", + "propertyProfile": "container" + }, + { + "id": "modelActions", + "propertyProfile": "container" + }, + { + "id": "modelDetails", + "propertyProfile": "container" + }, + { + "id": "logoutConfirm", + "propertyProfile": "container" + }, + { + "id": "logoutActions", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "view", + "attribute": "data-bf-view", + "values": [ + "selection", + "settings" + ] + }, + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "starting", + "pending", + "cancelling", + "failed" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + } + ] + }, + { + "id": "external-sources-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "hostMode", + "propertyProfile": "container" + }, + { + "id": "notice", + "propertyProfile": "container" + }, + { + "id": "policyCard", + "propertyProfile": "container" + }, + { + "id": "policySummary", + "propertyProfile": "container" + }, + { + "id": "policyRecovery", + "propertyProfile": "container" + }, + { + "id": "policyActions", + "propertyProfile": "container" + }, + { + "id": "diagnostics", + "propertyProfile": "container" + }, + { + "id": "sourceGroup", + "propertyProfile": "container" + }, + { + "id": "sourceDescription", + "propertyProfile": "container" + }, + { + "id": "sourceMembers", + "propertyProfile": "container" + }, + { + "id": "sourceControl", + "propertyProfile": "container" + }, + { + "id": "state", + "propertyProfile": "container" + }, + { + "id": "toolCard", + "propertyProfile": "container" + }, + { + "id": "toolTitle", + "propertyProfile": "container" + }, + { + "id": "toolDetail", + "propertyProfile": "container" + }, + { + "id": "toolWarning", + "propertyProfile": "container" + }, + { + "id": "toolActions", + "propertyProfile": "container" + }, + { + "id": "conflict", + "propertyProfile": "container" + }, + { + "id": "conflictTitle", + "propertyProfile": "container" + }, + { + "id": "conflictOptions", + "propertyProfile": "container" + }, + { + "id": "candidate", + "propertyProfile": "container" + }, + { + "id": "candidateState", + "propertyProfile": "container" + }, + { + "id": "candidateDetail", + "propertyProfile": "container" + }, + { + "id": "conflictHint", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "ecosystemCard", + "propertyProfile": "container" + }, + { + "id": "ecosystemHeading", + "propertyProfile": "container" + }, + { + "id": "ecosystemName", + "propertyProfile": "container" + }, + { + "id": "ecosystemState", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "acp-agents-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "manager", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "jsonEditor", + "propertyProfile": "container" + }, + { + "id": "jsonActions", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "registryList", + "propertyProfile": "container" + }, + { + "id": "registryRow", + "propertyProfile": "container" + }, + { + "id": "registryMain", + "propertyProfile": "container" + }, + { + "id": "capabilities", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "confirmation", + "propertyProfile": "container" + }, + { + "id": "remoteList", + "propertyProfile": "container" + }, + { + "id": "remoteServer", + "propertyProfile": "container" + }, + { + "id": "remoteHeader", + "propertyProfile": "container" + }, + { + "id": "remoteAgents", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "view", + "attribute": "data-bf-view", + "values": [ + "registry", + "json" + ] + } + ], + "states": [] + }, + { + "id": "session-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "control", + "propertyProfile": "container" + }, + { + "id": "petPicker", + "propertyProfile": "container" + }, + { + "id": "petChooser", + "propertyProfile": "container" + }, + { + "id": "petTrigger", + "propertyProfile": "container" + }, + { + "id": "petList", + "propertyProfile": "container" + }, + { + "id": "petGroup", + "propertyProfile": "container" + }, + { + "id": "petOption", + "propertyProfile": "container" + }, + { + "id": "petOptionMain", + "propertyProfile": "container" + }, + { + "id": "petActions", + "propertyProfile": "container" + }, + { + "id": "platformNote", + "propertyProfile": "container" + }, + { + "id": "debugInputs", + "propertyProfile": "container" + }, + { + "id": "debugActions", + "propertyProfile": "container" + }, + { + "id": "templateModal", + "propertyProfile": "container" + }, + { + "id": "templateCard", + "propertyProfile": "container" + }, + { + "id": "templateHeader", + "propertyProfile": "container" + }, + { + "id": "templateInfo", + "propertyProfile": "container" + }, + { + "id": "templateContent", + "propertyProfile": "container" + }, + { + "id": "templateField", + "propertyProfile": "container" + }, + { + "id": "templateNotes", + "propertyProfile": "container" + }, + { + "id": "modalFooter", + "propertyProfile": "container" + }, + { + "id": "restartModal", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "view", + "attribute": "data-bf-view", + "values": [ + "personalization", + "permissions" + ] + } + ], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "session-title-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "enableControl", + "propertyProfile": "container" + }, + { + "id": "modelControl", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "mcp-tools-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "statusBadge", + "propertyProfile": "container" + }, + { + "id": "serverDetails", + "propertyProfile": "container" + }, + { + "id": "detailItem", + "propertyProfile": "container" + }, + { + "id": "detailLabel", + "propertyProfile": "container" + }, + { + "id": "detailValue", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "jsonEditor", + "propertyProfile": "container" + }, + { + "id": "jsonHeader", + "propertyProfile": "container" + }, + { + "id": "jsonHint", + "propertyProfile": "container" + }, + { + "id": "jsonTextarea", + "propertyProfile": "container" + }, + { + "id": "jsonActions", + "propertyProfile": "container" + }, + { + "id": "examples", + "propertyProfile": "container" + }, + { + "id": "example", + "propertyProfile": "container" + }, + { + "id": "authEditor", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "view", + "attribute": "data-bf-view", + "values": [ + "list", + "json" + ] + } + ], + "states": [] + }, + { + "id": "external-mcp-overview", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "statusBadge", + "propertyProfile": "container" + }, + { + "id": "serverDetails", + "propertyProfile": "container" + }, + { + "id": "detailItem", + "propertyProfile": "container" + }, + { + "id": "detailLabel", + "propertyProfile": "container" + }, + { + "id": "detailValue", + "propertyProfile": "container" + }, + { + "id": "import", + "propertyProfile": "container" + }, + { + "id": "importPlan", + "propertyProfile": "container" + }, + { + "id": "importList", + "propertyProfile": "container" + }, + { + "id": "importOption", + "propertyProfile": "container" + }, + { + "id": "importOptionContent", + "propertyProfile": "container" + }, + { + "id": "importOptionMeta", + "propertyProfile": "container" + }, + { + "id": "importActions", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "pending", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"pending\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "muted", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"muted\"]" + } + } + ] + }, + { + "id": "voice-input-diagnostics", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "deviceControl", + "propertyProfile": "container" + }, + { + "id": "deviceSelect", + "propertyProfile": "container" + }, + { + "id": "diagnosticAction", + "propertyProfile": "container" + }, + { + "id": "level", + "propertyProfile": "container" + }, + { + "id": "levelValue", + "propertyProfile": "container" + }, + { + "id": "recognitionTest", + "propertyProfile": "container" + }, + { + "id": "note", + "propertyProfile": "container" + }, + { + "id": "result", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "phase", + "attribute": "data-bf-phase", + "values": [ + "idle", + "preparing-microphone", + "checking-microphone", + "preparing-recognition", + "recording", + "transcribing" + ] + }, + { + "id": "volume", + "attribute": "data-bf-volume", + "values": [ + "silent", + "low", + "normal" + ] + } + ], + "states": [ + { + "id": "testingMicrophone", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"testing-microphone\"]" + } + }, + { + "id": "testingRecognition", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"testing-recognition\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "assistant-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "avatar", + "propertyProfile": "container" + }, + { + "id": "headerInfo", + "propertyProfile": "container" + }, + { + "id": "name", + "propertyProfile": "container" + }, + { + "id": "primaryBadge", + "propertyProfile": "container" + }, + { + "id": "badges", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "vibe", + "propertyProfile": "container" + }, + { + "id": "configure", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "newSession", + "propertyProfile": "container" + }, + { + "id": "delete", + "propertyProfile": "container" + }, + { + "id": "chevron", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "primary", + "attribute": "data-bf-primary", + "values": [ + "true", + "false" + ] + } + ], + "states": [ + { + "id": "busy", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"busy\"]" + } + } + ] + }, + { + "id": "miniapp-customize-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "notice", + "propertyProfile": "container" + }, + { + "id": "noticeActions", + "propertyProfile": "container" + }, + { + "id": "request", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "chat", + "propertyProfile": "container" + }, + { + "id": "chatLoading", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "busy", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "stage", + "attribute": "data-bf-stage", + "values": [ + "idle", + "notice", + "drafting", + "preview", + "permission-review", + "applying" + ] + } + ], + "states": [ + { + "id": "busy", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"busy\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "previewOpen", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"preview-open\"]" + } + } + ] + }, + { + "id": "user-message-edit-composer", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "input", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "spinner", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "mode", + "attribute": "data-bf-mode", + "values": [ + "rich", + "plain" + ] + }, + { + "id": "action", + "attribute": "data-bf-action", + "values": [ + "cancel", + "submit" + ] + } + ], + "states": [ + { + "id": "submitting", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"submitting\"]" + } + } + ] + }, + { + "id": "tiptap-editor", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "inlineAi", + "propertyProfile": "container" + }, + { + "id": "inlineAiSurface", + "propertyProfile": "container" + }, + { + "id": "inlineAiPanel", + "propertyProfile": "container" + }, + { + "id": "composer", + "propertyProfile": "container" + }, + { + "id": "quickActions", + "propertyProfile": "container" + }, + { + "id": "quickAction", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "workspace-project-permissions-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "intro", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "sectionHeader", + "propertyProfile": "container" + }, + { + "id": "grants", + "propertyProfile": "container" + }, + { + "id": "grant", + "propertyProfile": "container" + }, + { + "id": "rules", + "propertyProfile": "container" + }, + { + "id": "rule", + "propertyProfile": "container" + }, + { + "id": "ruleActions", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "workspace-session-batch-modal", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "hero", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "toolbarActions", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "state", + "propertyProfile": "container" + }, + { + "id": "row", + "propertyProfile": "container" + }, + { + "id": "rowCheck", + "propertyProfile": "container" + }, + { + "id": "rowContent", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "child", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"child\"]" + } + } + ] + }, + { + "id": "archived-sessions-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "group", + "propertyProfile": "container" + }, + { + "id": "groupHeader", + "propertyProfile": "container" + }, + { + "id": "groupList", + "propertyProfile": "container" + }, + { + "id": "row", + "propertyProfile": "container" + }, + { + "id": "rowInfo", + "propertyProfile": "container" + }, + { + "id": "rowActions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "collapsed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + } + ] + }, + { + "id": "settings-nav", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "panel", + "continuityGroup": "settings-workspace" + }, + { + "id": "header", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "settings-workspace" + }, + { + "id": "search", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "sections", + "propertyProfile": "container", + "visualRole": "content", + "continuityGroup": "settings-workspace" + }, + { + "id": "searchEmpty", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "searchResults", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "searchResult", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "category", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "categoryHeader", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "items", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "item", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "highlight", + "propertyProfile": "paint", + "visualRole": "decoration" + } + ], + "facets": [], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "background-command-output-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "notice", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "output", + "propertyProfile": "container" + }, + { + "id": "terminal", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "inputEditor", + "propertyProfile": "container" + }, + { + "id": "inputOptions", + "propertyProfile": "container" + }, + { + "id": "inputActions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "chat-input-pixel-pet", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "stage", + "propertyProfile": "container" + }, + { + "id": "svg", + "propertyProfile": "container" + }, + { + "id": "silhouette", + "propertyProfile": "container" + }, + { + "id": "face", + "propertyProfile": "container" + }, + { + "id": "rest", + "propertyProfile": "container" + }, + { + "id": "analyze", + "propertyProfile": "container" + }, + { + "id": "wait", + "propertyProfile": "container" + }, + { + "id": "work", + "propertyProfile": "container" + }, + { + "id": "hover", + "propertyProfile": "container" + }, + { + "id": "drag", + "propertyProfile": "container" + }, + { + "id": "petdex", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "mood", + "attribute": "data-bf-mood", + "values": [ + "rest", + "analyzing", + "waiting", + "working", + "hover", + "dragging" + ] + }, + { + "id": "layout", + "attribute": "data-bf-layout", + "values": [ + "default", + "center", + "stopRight", + "petdex" + ] + } + ], + "states": [] + }, + { + "id": "file-mention-picker", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "back", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemName", + "propertyProfile": "container" + }, + { + "id": "itemDetail", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "session-file-modifications-bar", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "toggle", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "file", + "propertyProfile": "container" + }, + { + "id": "fileIcon", + "propertyProfile": "container" + }, + { + "id": "fileName", + "propertyProfile": "container" + }, + { + "id": "fileSource", + "propertyProfile": "container" + }, + { + "id": "fileError", + "propertyProfile": "container" + }, + { + "id": "fileStats", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "layout", + "attribute": "data-bf-layout", + "values": [ + "default", + "compact" + ] + }, + { + "id": "operation", + "attribute": "data-bf-operation", + "values": [ + "write", + "delete", + "edit" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "editor-breadcrumb", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "separator", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemIcon", + "propertyProfile": "container" + }, + { + "id": "itemText", + "propertyProfile": "container" + }, + { + "id": "menu", + "propertyProfile": "container" + }, + { + "id": "menuHeader", + "propertyProfile": "container" + }, + { + "id": "menuBack", + "propertyProfile": "container" + }, + { + "id": "menuTitle", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "listItem", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "git-branch-history", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerLeft", + "propertyProfile": "container" + }, + { + "id": "headerRight", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "commits", + "propertyProfile": "container" + }, + { + "id": "commit", + "propertyProfile": "container" + }, + { + "id": "commitMain", + "propertyProfile": "container" + }, + { + "id": "timeline", + "propertyProfile": "container" + }, + { + "id": "commitInfo", + "propertyProfile": "container" + }, + { + "id": "commitDetails", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "git-diff-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerLeft", + "propertyProfile": "container" + }, + { + "id": "headerRight", + "propertyProfile": "container" + }, + { + "id": "typeSwitcher", + "propertyProfile": "container" + }, + { + "id": "typeOption", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "fileList", + "propertyProfile": "container" + }, + { + "id": "file", + "propertyProfile": "container" + }, + { + "id": "fileHeader", + "propertyProfile": "container" + }, + { + "id": "fileInfo", + "propertyProfile": "container" + }, + { + "id": "fileStats", + "propertyProfile": "container" + }, + { + "id": "diffContent", + "propertyProfile": "container" + }, + { + "id": "diffLine", + "propertyProfile": "container" + }, + { + "id": "lineNumber", + "propertyProfile": "container" + }, + { + "id": "lineContent", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + } + ] + }, + { + "id": "git-settings-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerLeft", + "propertyProfile": "container" + }, + { + "id": "headerRight", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "tabs", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "formGroup", + "propertyProfile": "container" + }, + { + "id": "configItem", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "quick-actions-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "dialog", + "propertyProfile": "container" + }, + { + "id": "dialogIcon", + "propertyProfile": "container" + }, + { + "id": "field", + "propertyProfile": "container" + }, + { + "id": "dialogFooter", + "propertyProfile": "container" + }, + { + "id": "row", + "propertyProfile": "container" + }, + { + "id": "rowIcon", + "propertyProfile": "container" + }, + { + "id": "rowBody", + "propertyProfile": "container" + }, + { + "id": "rowControls", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "status-bar-popover", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "hint", + "propertyProfile": "container" + }, + { + "id": "inputWrap", + "propertyProfile": "container" + }, + { + "id": "input", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemIcon", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "kind", + "attribute": "data-bf-popover", + "values": [ + "line", + "indent", + "encoding", + "language" + ] + } + ], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "m-editor", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "editPanel", + "propertyProfile": "container" + }, + { + "id": "previewPanel", + "propertyProfile": "container" + }, + { + "id": "irPanel", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "references-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "close", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "fileGroup", + "propertyProfile": "container" + }, + { + "id": "fileHeader", + "propertyProfile": "container" + }, + { + "id": "filePath", + "propertyProfile": "container" + }, + { + "id": "fileCount", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "line", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "nav-search-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "card", + "propertyProfile": "container" + }, + { + "id": "inputRow", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "results", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "group", + "propertyProfile": "container" + }, + { + "id": "groupLabel", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemIcon", + "propertyProfile": "container" + }, + { + "id": "itemContent", + "propertyProfile": "container" + }, + { + "id": "itemLabel", + "propertyProfile": "container" + }, + { + "id": "itemSublabel", + "propertyProfile": "container" + }, + { + "id": "sessionHint", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + } + ] + }, + { + "id": "workspace-related-paths-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "intro", + "propertyProfile": "container" + }, + { + "id": "introCopy", + "propertyProfile": "container" + }, + { + "id": "scope", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "card", + "propertyProfile": "container" + }, + { + "id": "cardHeader", + "propertyProfile": "container" + }, + { + "id": "pathRow", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "footerActions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "branch-select-modal", + "parts": [ + { + "id": "overlay", + "propertyProfile": "container" + }, + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "close", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "input", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemName", + "propertyProfile": "container" + }, + { + "id": "badge", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "options", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "current", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"current\"]" + } + } + ] + }, + { + "id": "floating-mini-chat", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "backdrop", + "propertyProfile": "container" + }, + { + "id": "trigger", + "propertyProfile": "container" + }, + { + "id": "triggerActivity", + "propertyProfile": "container" + }, + { + "id": "triggerIcon", + "propertyProfile": "container" + }, + { + "id": "panel", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "sessionIcon", + "propertyProfile": "container" + }, + { + "id": "headerAction", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "pending", + "propertyProfile": "container" + }, + { + "id": "pendingIcon", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "mode", + "attribute": "data-bf-mode", + "values": [ + "chat", + "miniapp" + ] + } + ], + "states": [ + { + "id": "open", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "processing", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"processing\"]" + } + }, + { + "id": "customizing", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"customizing\"]" + } + } + ] + }, + { + "id": "miniapp-bubble-welcome", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "eyebrow", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + }, + { + "id": "workspace", + "propertyProfile": "container" + }, + { + "id": "suggestions", + "propertyProfile": "container" + }, + { + "id": "suggestionsLabel", + "propertyProfile": "container" + }, + { + "id": "suggestionsList", + "propertyProfile": "container" + }, + { + "id": "suggestion", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "branches-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + }, + { + "id": "left", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "branch", + "propertyProfile": "container" + }, + { + "id": "branchInfo", + "propertyProfile": "container" + }, + { + "id": "branchActions", + "propertyProfile": "container" + }, + { + "id": "right", + "propertyProfile": "container" + }, + { + "id": "historyToolbar", + "propertyProfile": "container" + }, + { + "id": "historyTitle", + "propertyProfile": "container" + }, + { + "id": "historyList", + "propertyProfile": "container" + }, + { + "id": "commit", + "propertyProfile": "container" + }, + { + "id": "commitHeader", + "propertyProfile": "container" + }, + { + "id": "commitInfo", + "propertyProfile": "container" + }, + { + "id": "commitActions", + "propertyProfile": "container" + }, + { + "id": "commitDetails", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "current", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"current\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "miniapp-gallery-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "categoryFilters", + "propertyProfile": "container" + }, + { + "id": "categoryFilter", + "propertyProfile": "container" + }, + { + "id": "detailTags", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "miniapp-market-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "card", + "propertyProfile": "container" + }, + { + "id": "detail", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "miniapp-submissions-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "form", + "propertyProfile": "container" + }, + { + "id": "history", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "shell-nav", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "splitButton", + "propertyProfile": "container" + }, + { + "id": "menu", + "propertyProfile": "container" + }, + { + "id": "menuItem", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "mission-control", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "filters", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "groupFilters", + "propertyProfile": "container" + }, + { + "id": "filter", + "propertyProfile": "container" + }, + { + "id": "grid", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "separator", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "group", + "attribute": "data-bf-group", + "values": [ + "primary", + "secondary", + "tertiary" + ] + } + ], + "states": [ + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + } + ] + }, + { + "id": "canvas-tab", + "parts": [ + { + "id": "root", + "propertyProfile": "control", + "visualRole": "continuous-surface", + "continuityGroup": "canvas-tabs" + }, + { + "id": "typeIcon", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "title", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "dirtyIndicator", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "action", + "propertyProfile": "control", + "visualRole": "control" + } + ], + "facets": [ + { + "id": "group", + "attribute": "data-bf-group", + "values": [ + "primary", + "secondary", + "tertiary" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "dirty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dirty\"]" + } + }, + { + "id": "dragging", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dragging\"]" + } + }, + { + "id": "deleted", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"deleted\"]" + } + }, + { + "id": "pinned", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"pinned\"]" + } + }, + { + "id": "preview", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"preview\"]" + } + } + ] + }, + { + "id": "canvas-tab-bar", + "parts": [ + { + "id": "root", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "canvas-tabs" + }, + { + "id": "list", + "propertyProfile": "container", + "visualRole": "continuous-surface", + "continuityGroup": "canvas-tabs" + }, + { + "id": "tabWrapper", + "propertyProfile": "container", + "visualRole": "continuous-surface", + "continuityGroup": "canvas-tabs" + }, + { + "id": "dropIndicator", + "propertyProfile": "overlay", + "visualRole": "divider" + }, + { + "id": "actions", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "action", + "propertyProfile": "control", + "visualRole": "control" + } + ], + "facets": [ + { + "id": "group", + "attribute": "data-bf-group", + "values": [ + "primary", + "secondary", + "tertiary" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + } + ] + }, + { + "id": "chat-input-workspace-strip", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "workspace", + "propertyProfile": "container" + }, + { + "id": "branch", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "permission", + "propertyProfile": "container" + }, + { + "id": "permissionMenu", + "propertyProfile": "container" + }, + { + "id": "permissionOptions", + "propertyProfile": "container" + }, + { + "id": "permissionOption", + "propertyProfile": "container" + }, + { + "id": "usageAction", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "code-preview", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "plain", + "propertyProfile": "container" + }, + { + "id": "line", + "propertyProfile": "container" + }, + { + "id": "lineNumber", + "propertyProfile": "container" + }, + { + "id": "lineContent", + "propertyProfile": "container" + }, + { + "id": "cursor", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + }, + { + "id": "streaming", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"streaming\"]" + } + }, + { + "id": "highlighted", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"highlighted\"]" + } + } + ] + }, + { + "id": "explore-group", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "contentWrapper", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "pending-queue-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "editor", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "editing", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"editing\"]" + } + }, + { + "id": "sending", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"sending\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + } + ] + }, + { + "id": "subagent-projection", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "truncated", + "propertyProfile": "container" + }, + { + "id": "hint", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + }, + { + "id": "expandAction", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "collapse", + "propertyProfile": "container" + }, + { + "id": "container", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "collapsed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "thread-goal-dialogs", + "parts": [ + { + "id": "menu", + "propertyProfile": "overlay", + "visualRole": "dialog" + }, + { + "id": "header", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "thread-goal-dialog" + }, + { + "id": "section", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "objective", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "workflow", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "workflowItem", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "footer", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "thread-goal-dialog" + }, + { + "id": "actions", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "hint", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "edit", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "resume", + "propertyProfile": "control", + "visualRole": "control" + } + ], + "facets": [], + "states": [ + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "welcome-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "workspace" + }, + { + "id": "content", + "propertyProfile": "container", + "visualRole": "card" + }, + { + "id": "greeting", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "mascot", + "propertyProfile": "paint", + "visualRole": "decoration" + }, + { + "id": "heading", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "tagline", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "divider", + "propertyProfile": "paint", + "visualRole": "divider" + }, + { + "id": "narrative", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "openWorkspaceAction", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "workspaceAction", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "gitAction", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "workspaceMenu", + "propertyProfile": "overlay", + "visualRole": "popup" + }, + { + "id": "workspaceItem", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "cowork", + "propertyProfile": "container", + "visualRole": "content" + } + ], + "facets": [], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover:not(:disabled)" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": ":disabled" + } + }, + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + } + ] + }, + { + "id": "generative-widget-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "extra", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "exportAction", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "captureRoot", + "propertyProfile": "container" + }, + { + "id": "exportStage", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + }, + { + "id": "exporting", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"exporting\"]" + } + } + ] + }, + { + "id": "git-tool-display", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "headerExtra", + "propertyProfile": "container" + }, + { + "id": "info", + "propertyProfile": "container" + }, + { + "id": "result", + "propertyProfile": "container" + }, + { + "id": "output", + "propertyProfile": "container" + }, + { + "id": "stderr", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + } + ] + }, + { + "id": "snapshot-fullscreen-diff-viewer", + "parts": [ + { + "id": "overlay", + "propertyProfile": "container" + }, + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "sessionInfo", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "navigation", + "propertyProfile": "container" + }, + { + "id": "tabs", + "propertyProfile": "container" + }, + { + "id": "tab", + "propertyProfile": "container" + }, + { + "id": "currentFile", + "propertyProfile": "container" + }, + { + "id": "fileInfo", + "propertyProfile": "container" + }, + { + "id": "fileActions", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + } + ] + }, + { + "id": "mermaid-block", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "streaming", + "propertyProfile": "container" + }, + { + "id": "codePreview", + "propertyProfile": "container" + }, + { + "id": "code", + "propertyProfile": "container" + }, + { + "id": "hint", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "rendered", + "propertyProfile": "container" + }, + { + "id": "diagram", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "source", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "streaming", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"streaming\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + }, + { + "id": "copied", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"copied\"]" + } + } + ] + }, + { + "id": "default-model-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "primaryModel", + "propertyProfile": "container" + }, + { + "id": "lightweightModel", + "propertyProfile": "container" + }, + { + "id": "embeddingModel", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "global-permission-rules-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "intro", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "sectionHeader", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "rules", + "propertyProfile": "container" + }, + { + "id": "rule", + "propertyProfile": "container" + }, + { + "id": "ruleActions", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "mcp-resource-browser", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "headerActions", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "resource", + "propertyProfile": "container" + }, + { + "id": "resourceIcon", + "propertyProfile": "container" + }, + { + "id": "resourceInfo", + "propertyProfile": "container" + }, + { + "id": "viewer", + "propertyProfile": "container" + }, + { + "id": "viewerHeader", + "propertyProfile": "container" + }, + { + "id": "viewerContent", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "editor-status-bar", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "left", + "propertyProfile": "container" + }, + { + "id": "right", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "selection", + "propertyProfile": "container" + }, + { + "id": "separator", + "propertyProfile": "container" + }, + { + "id": "lsp", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "image-viewer", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "info", + "propertyProfile": "container" + }, + { + "id": "controls", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "container", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "imageWrapper", + "propertyProfile": "container" + }, + { + "id": "image", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "fullscreen", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"fullscreen\"]" + } + } + ] + }, + { + "id": "branch-quick-switch", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "search", + "propertyProfile": "container" + }, + { + "id": "input", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemName", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + }, + { + "id": "current", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"current\"]" + } + } + ] + }, + { + "id": "workspace-list-section", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "dropLine", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "dragging", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dragging\"]" + } + }, + { + "id": "selected", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"selected\"]" + } + } + ] + }, + { + "id": "workspace-item", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "card", + "propertyProfile": "container" + }, + { + "id": "collapse", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "name", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "badge", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "menu", + "propertyProfile": "container" + }, + { + "id": "menuTrigger", + "propertyProfile": "container" + }, + { + "id": "menuPopover", + "propertyProfile": "container" + }, + { + "id": "menuItem", + "propertyProfile": "container" + }, + { + "id": "menuDivider", + "propertyProfile": "container" + }, + { + "id": "sessions", + "propertyProfile": "container" + }, + { + "id": "remoteStatus", + "propertyProfile": "container" + }, + { + "id": "indexIndicator", + "propertyProfile": "container" + }, + { + "id": "indexPanel", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "variant", + "attribute": "data-bf-variant", + "values": [ + "workspace", + "assistant" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "dragging", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"dragging\"]" + } + }, + { + "id": "collapsed", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + }, + { + "id": "remote", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"remote\"]" + } + }, + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + } + ] + }, + { + "id": "new-project-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "hero", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "field", + "propertyProfile": "container" + }, + { + "id": "pathSelector", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "canvas-editor-area", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "primary", + "propertyProfile": "container" + }, + { + "id": "secondary", + "propertyProfile": "container" + }, + { + "id": "tertiary", + "propertyProfile": "container" + }, + { + "id": "topRow", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "layout", + "attribute": "data-bf-layout", + "values": [ + "none", + "horizontal", + "vertical", + "grid" + ] + } + ], + "states": [] + }, + { + "id": "canvas-editor-group", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "tabContent", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "emptyContent", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "group", + "attribute": "data-bf-group", + "values": [ + "primary", + "secondary", + "tertiary" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + } + ] + }, + { + "id": "canvas-thumbnail", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "code", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + }, + { + "id": "groupBadge", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "group", + "attribute": "data-bf-group", + "values": [ + "primary", + "secondary", + "tertiary" + ] + } + ], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "dirty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dirty\"]" + } + }, + { + "id": "deleted", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"deleted\"]" + } + }, + { + "id": "pinned", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"pinned\"]" + } + }, + { + "id": "preview", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"preview\"]" + } + } + ] + }, + { + "id": "canvas-tab-overflow", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "trigger", + "propertyProfile": "container" + }, + { + "id": "badge", + "propertyProfile": "container" + }, + { + "id": "menu", + "propertyProfile": "container" + }, + { + "id": "missionControl", + "propertyProfile": "container" + }, + { + "id": "divider", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemTitle", + "propertyProfile": "container" + }, + { + "id": "itemClose", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "dirty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dirty\"]" + } + }, + { + "id": "deleted", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"deleted\"]" + } + } + ] + }, + { + "id": "browser-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "browser-panel" + }, + { + "id": "toolbar", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "browser-panel" + }, + { + "id": "address", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "error", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "content", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "browser-panel" + }, + { + "id": "iframe", + "propertyProfile": "layout", + "visualRole": "content" + }, + { + "id": "webviewHost", + "propertyProfile": "layout", + "visualRole": "content" + }, + { + "id": "placeholder", + "propertyProfile": "container", + "visualRole": "content" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "nursery-gallery", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "defaults", + "propertyProfile": "container" + }, + { + "id": "avatar", + "propertyProfile": "container" + }, + { + "id": "defaultsContent", + "propertyProfile": "container" + }, + { + "id": "stats", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "deep-review-consent-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "heading", + "propertyProfile": "container" + }, + { + "id": "close", + "propertyProfile": "container" + }, + { + "id": "capacityNote", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "summaryHeader", + "propertyProfile": "container" + }, + { + "id": "summaryStats", + "propertyProfile": "container" + }, + { + "id": "impactGrid", + "propertyProfile": "container" + }, + { + "id": "strategy", + "propertyProfile": "container" + }, + { + "id": "reviewerGroup", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "flow-text-block", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "textContent", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "mode", + "attribute": "data-bf-mode", + "values": [ + "markdown", + "text" + ] + } + ], + "states": [ + { + "id": "streaming", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"streaming\"]" + } + } + ] + }, + { + "id": "permission-request-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "collapsedTrigger", + "propertyProfile": "container" + }, + { + "id": "collapsedBadge", + "propertyProfile": "container" + }, + { + "id": "panel", + "propertyProfile": "container" + }, + { + "id": "heading", + "propertyProfile": "container" + }, + { + "id": "headingActions", + "propertyProfile": "container" + }, + { + "id": "requests", + "propertyProfile": "container" + }, + { + "id": "request", + "propertyProfile": "container" + }, + { + "id": "requestHeading", + "propertyProfile": "container" + }, + { + "id": "requestDetails", + "propertyProfile": "container" + }, + { + "id": "risk", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "feedback", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "singleActions", + "propertyProfile": "container" + }, + { + "id": "batchActions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "collapsed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + }, + { + "id": "responding", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"responding\"]" + } + }, + { + "id": "error", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"error\"]" + } + } + ] + }, + { + "id": "user-message", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "inlineContent", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "timestamp", + "propertyProfile": "container" + }, + { + "id": "snapshotAction", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "collapsed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + } + ] + }, + { + "id": "tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "surface", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "extra", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "pending", + "queued", + "waiting", + "preparing", + "streaming", + "receiving", + "running", + "completed", + "error", + "cancelled", + "rejected", + "analyzing", + "pending_confirmation", + "confirmed" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "confirmation", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"confirmation\"]" + } + } + ] + }, + { + "id": "canvas-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "extra", + "propertyProfile": "container" + }, + { + "id": "diagnostics", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "sourcePreview", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "diagnosticList", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "clickable", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"clickable\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "computer-use-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "permissionDenied", + "propertyProfile": "container" + }, + { + "id": "settingsButton", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "row", + "propertyProfile": "container" + }, + { + "id": "loopWarning", + "propertyProfile": "container" + }, + { + "id": "actionCode", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + } + ] + }, + { + "id": "default-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "metaLabel", + "propertyProfile": "container" + }, + { + "id": "metaDescription", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "sectionLabel", + "propertyProfile": "container" + }, + { + "id": "code", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "confirmation", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"confirmation\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + } + ] + }, + { + "id": "get-file-diff-display", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "info", + "propertyProfile": "container" + }, + { + "id": "fileName", + "propertyProfile": "container" + }, + { + "id": "diffType", + "propertyProfile": "container" + }, + { + "id": "stats", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + } + ] + }, + { + "id": "mini-app-tool-display", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "info", + "propertyProfile": "container" + }, + { + "id": "operation", + "propertyProfile": "container" + }, + { + "id": "command", + "propertyProfile": "container" + }, + { + "id": "output", + "propertyProfile": "container" + }, + { + "id": "errorIndicator", + "propertyProfile": "container" + }, + { + "id": "result", + "propertyProfile": "container" + }, + { + "id": "rows", + "propertyProfile": "container" + }, + { + "id": "row", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "value", + "propertyProfile": "container" + }, + { + "id": "footer", + "propertyProfile": "container" + }, + { + "id": "open", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "model-thinking-display", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "chevron", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "expandContainer", + "propertyProfile": "container" + }, + { + "id": "contentWrapper", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "context", + "attribute": "data-bf-context", + "values": [ + "default", + "subagent-projection" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "streaming", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"streaming\"]" + } + } + ] + }, + { + "id": "todo-write-display", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "compactIcon", + "propertyProfile": "container" + }, + { + "id": "count", + "propertyProfile": "container" + }, + { + "id": "progress", + "propertyProfile": "container" + }, + { + "id": "headerContent", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemContent", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "mode", + "attribute": "data-bf-mode", + "values": [ + "compact", + "standard" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "completed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"completed\"]" + } + } + ] + }, + { + "id": "tool-timeout-indicator", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "duration", + "propertyProfile": "container" + }, + { + "id": "elapsed", + "propertyProfile": "container" + }, + { + "id": "timeout", + "propertyProfile": "container" + }, + { + "id": "controls", + "propertyProfile": "container" + }, + { + "id": "toggle", + "propertyProfile": "container" + }, + { + "id": "popover", + "propertyProfile": "container" + }, + { + "id": "option", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "mode", + "attribute": "data-bf-mode", + "values": [ + "completed", + "live" + ] + } + ], + "states": [ + { + "id": "warning", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"warning\"]" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"disabled\"]" + } + }, + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + } + ] + }, + { + "id": "web-fetch-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "headerUrl", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "detailsRow", + "propertyProfile": "container" + }, + { + "id": "details", + "propertyProfile": "container" + }, + { + "id": "detail", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "result", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + } + ] + }, + { + "id": "compact-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "surface", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "extra", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "pending", + "queued", + "waiting", + "preparing", + "streaming", + "receiving", + "running", + "completed", + "error", + "cancelled", + "rejected", + "analyzing", + "pending_confirmation", + "confirmed" + ] + } + ], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + }, + { + "id": "clickable", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"clickable\"]" + } + }, + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "tool-card-status-slot", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "statusLayer", + "propertyProfile": "container" + }, + { + "id": "iconLayer", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "defaultIcon", + "attribute": "data-bf-default-icon", + "values": [ + "status", + "tool" + ] + } + ], + "states": [] + }, + { + "id": "tool-card-header-actions", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "acp-permission-actions", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "presentation", + "attribute": "data-bf-presentation", + "values": [ + "icon", + "text" + ] + }, + { + "id": "decision", + "attribute": "data-bf-decision", + "values": [ + "allow", + "reject" + ] + } + ], + "states": [ + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": ":disabled" + } + } + ] + }, + { + "id": "context-compression-display", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "info", + "propertyProfile": "container" + }, + { + "id": "tokenStat", + "propertyProfile": "container" + }, + { + "id": "savings", + "propertyProfile": "container" + }, + { + "id": "processing", + "propertyProfile": "container" + }, + { + "id": "meta", + "propertyProfile": "container" + }, + { + "id": "detail", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + }, + { + "id": "fallback", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"fallback\"]" + } + } + ] + }, + { + "id": "get-tool-spec-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "expanded", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "description", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + } + ] + }, + { + "id": "view-image-tool-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "image", + "propertyProfile": "container" + }, + { + "id": "lightbox", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + }, + { + "id": "failed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"failed\"]" + } + } + ] + }, + { + "id": "acp-plan-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "progress", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "completed", + "in_progress", + "pending" + ] + } + ], + "states": [] + }, + { + "id": "background-command-input-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "summaryLabel", + "propertyProfile": "container" + }, + { + "id": "command", + "propertyProfile": "container" + }, + { + "id": "options", + "propertyProfile": "container" + }, + { + "id": "note", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "sending", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"sending\"]" + } + }, + { + "id": "masked", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"masked\"]" + } + } + ] + }, + { + "id": "chat-empty-state", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "container", + "propertyProfile": "container" + }, + { + "id": "greeting", + "propertyProfile": "container" + }, + { + "id": "divider", + "propertyProfile": "container" + }, + { + "id": "prompt", + "propertyProfile": "container" + }, + { + "id": "noWorkspace", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "copy-output-button", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "text", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "action", + "attribute": "data-bf-action", + "values": [ + "copy", + "edit" + ] + } + ], + "states": [ + { + "id": "copied", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"copied\"]" + } + } + ] + }, + { + "id": "copyable-text-preview", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "tooltipContent", + "propertyProfile": "container" + }, + { + "id": "tooltipText", + "propertyProfile": "container" + }, + { + "id": "copyAction", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "copied", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"copied\"]" + } + } + ] + }, + { + "id": "current-session-title", + "parts": [ + { + "id": "root", + "propertyProfile": "container", + "visualRole": "toolbar", + "continuityGroup": "session-header" + }, + { + "id": "title", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "create", + "propertyProfile": "control", + "visualRole": "control" + } + ], + "facets": [], + "states": [] + }, + { + "id": "cowork-example-cards", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "grid", + "propertyProfile": "container" + }, + { + "id": "card", + "propertyProfile": "container" + }, + { + "id": "cardHeader", + "propertyProfile": "container" + }, + { + "id": "cardIcon", + "propertyProfile": "container" + }, + { + "id": "cardTitle", + "propertyProfile": "container" + }, + { + "id": "cardDescription", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "image-analysis-card", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "thumbnail", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + }, + { + "id": "info", + "propertyProfile": "container" + }, + { + "id": "filename", + "propertyProfile": "container" + }, + { + "id": "status", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "expand", + "propertyProfile": "container" + }, + { + "id": "details", + "propertyProfile": "container" + }, + { + "id": "section", + "propertyProfile": "container" + }, + { + "id": "tags", + "propertyProfile": "container" + }, + { + "id": "metadata", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "status", + "attribute": "data-bf-status", + "values": [ + "analyzing", + "completed", + "error" + ] + } + ], + "states": [ + { + "id": "expanded", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"expanded\"]" + } + } + ] + }, + { + "id": "scroll-to-bottom-button", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "badge", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "scroll-to-latest-bar", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "gradient", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "button", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "input", + "attribute": "data-bf-input", + "values": [ + "active", + "expanded", + "collapsed" + ] + } + ], + "states": [] + }, + { + "id": "scroll-to-turn-header-button", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "gradient", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "button", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "visible", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"visible\"]" + } + } + ] + }, + { + "id": "token-usage-indicator", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "track", + "propertyProfile": "container" + }, + { + "id": "fill", + "propertyProfile": "container" + }, + { + "id": "hover", + "propertyProfile": "container" + }, + { + "id": "percentage", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "level", + "attribute": "data-bf-level", + "values": [ + "normal", + "warning", + "critical" + ] + } + ], + "states": [] + }, + { + "id": "tool-approval-bar", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "snapshot-rollback-button", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "outcome", + "attribute": "data-bf-outcome", + "values": [ + "idle", + "current", + "success", + "error" + ] + } + ], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "smart-recommendations", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "close", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "action", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "sticky-task-indicator", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "gradient", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "button", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + }, + { + "id": "arrow", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "visible", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"visible\"]" + } + } + ] + }, + { + "id": "turn-history-panel", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "count", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemHeader", + "propertyProfile": "container" + }, + { + "id": "files", + "propertyProfile": "container" + }, + { + "id": "filesList", + "propertyProfile": "container" + }, + { + "id": "time", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "current", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"current\"]" + } + } + ] + }, + { + "id": "turn-rollback-button", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "mode", + "attribute": "data-bf-mode", + "values": [ + "current", + "action" + ] + } + ], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "session-runtime-status-entry", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "label", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "runtime-status-slot", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "hint", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "session-menu", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "trigger", + "propertyProfile": "container" + }, + { + "id": "dropdown", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + }, + { + "id": "scroll", + "propertyProfile": "container" + }, + { + "id": "divider", + "propertyProfile": "container" + }, + { + "id": "item", + "propertyProfile": "container" + }, + { + "id": "itemIcon", + "propertyProfile": "container" + }, + { + "id": "itemLabel", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "itemKind", + "attribute": "data-bf-item-kind", + "values": [ + "create", + "session" + ] + }, + { + "id": "sessionKind", + "attribute": "data-bf-session-kind", + "values": [ + "code", + "cowork" + ] + } + ], + "states": [ + { + "id": "open", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + } + ] + }, + { + "id": "dispatch-install-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "dispatch-result-dialog", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "actions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "dispatch-target-picker", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "trigger", + "propertyProfile": "container" + }, + { + "id": "menu", + "propertyProfile": "container" + }, + { + "id": "option", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "voice-input-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "modelList", + "propertyProfile": "container" + }, + { + "id": "modelCard", + "propertyProfile": "container" + }, + { + "id": "modelActions", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "worktrees-config", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "results", + "propertyProfile": "container" + }, + { + "id": "project", + "propertyProfile": "container" + }, + { + "id": "worktreeList", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "turn-completion-notice", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "text", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "body", + "propertyProfile": "container" + }, + { + "id": "reason", + "propertyProfile": "container" + }, + { + "id": "reasonCode", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "tone", + "attribute": "data-bf-tone", + "values": [ + "error", + "info", + "warning" + ] + } + ], + "states": [] + }, + { + "id": "turn-failure-notice", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "icon", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "summary", + "propertyProfile": "container" + }, + { + "id": "title", + "propertyProfile": "container" + }, + { + "id": "message", + "propertyProfile": "container" + }, + { + "id": "toggle", + "propertyProfile": "container" + }, + { + "id": "details", + "propertyProfile": "container" + }, + { + "id": "facts", + "propertyProfile": "container" + }, + { + "id": "fact", + "propertyProfile": "container" + }, + { + "id": "rawError", + "propertyProfile": "container" + }, + { + "id": "rawHeader", + "propertyProfile": "container" + }, + { + "id": "copy", + "propertyProfile": "container" + }, + { + "id": "code", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "open", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"open\"]" + } + }, + { + "id": "copied", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"copied\"]" + } + } + ] + }, + { + "id": "virtual-item", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "imageAnalyzing", + "propertyProfile": "container" + }, + { + "id": "placeholder", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "searchMatch", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"searchMatch\"]" + } + }, + { + "id": "searchCurrent", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"searchCurrent\"]" + } + } + ] + } + ], + "scenes": [ + { + "id": "workbench", + "parts": [ + { + "id": "workspace", + "propertyProfile": "container" + }, + { + "id": "collapsedNav", + "propertyProfile": "container" + }, + { + "id": "navArea", + "propertyProfile": "container" + }, + { + "id": "navDivider", + "propertyProfile": "container" + }, + { + "id": "sceneArea", + "propertyProfile": "container" + }, + { + "id": "viewport", + "propertyProfile": "container" + }, + { + "id": "viewportClip", + "propertyProfile": "container" + }, + { + "id": "scene", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "sceneId", + "attribute": "data-bf-scene-id", + "values": [ + "welcome", + "session", + "terminal", + "git", + "settings", + "file-viewer", + "profile", + "agents", + "skills", + "miniapps", + "pages", + "browser", + "assistant", + "insights", + "shell", + "panel-view" + ] + } + ], + "states": [ + { + "id": "fullscreen", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"fullscreen\"]" + } + }, + { + "id": "toolbar", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"toolbar\"]" + } + }, + { + "id": "collapsed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"collapsed\"]" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + } + ] + }, + { + "id": "welcome", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "workspace" + }, + { + "id": "content", + "propertyProfile": "container", + "visualRole": "card" + }, + { + "id": "greeting", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "title", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "subtitle", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "divider", + "propertyProfile": "paint", + "visualRole": "divider" + }, + { + "id": "recentSection", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "sectionHeader", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "sectionLabel", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "actions", + "propertyProfile": "container", + "visualRole": "toolbar" + }, + { + "id": "action", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "recentList", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "recentRow", + "propertyProfile": "container", + "visualRole": "content" + }, + { + "id": "recentItem", + "propertyProfile": "control", + "visualRole": "control" + }, + { + "id": "recentName", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "recentMeta", + "propertyProfile": "paint", + "visualRole": "content" + }, + { + "id": "empty", + "propertyProfile": "container", + "visualRole": "content" + } + ], + "facets": [], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": ":disabled" + } + } + ] + }, + { + "id": "shell", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + } + ] + }, + { + "id": "agents", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "anchorBar", + "propertyProfile": "container" + }, + { + "id": "zones", + "propertyProfile": "container" + }, + { + "id": "coreGrid", + "propertyProfile": "container" + }, + { + "id": "filters", + "propertyProfile": "container" + }, + { + "id": "detailSection", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "assistant", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "browser", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "loading", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"loading\"]" + } + } + ] + }, + { + "id": "file-viewer", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "git", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "view", + "attribute": "data-bf-view", + "values": [ + "hidden", + "repository", + "not-repository", + "loading" + ] + } + ], + "states": [] + }, + { + "id": "miniapp-gallery", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "miniapp", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "runner", + "propertyProfile": "container" + }, + { + "id": "preview", + "propertyProfile": "container" + }, + { + "id": "notice", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "customizing", + "selector": { + "kind": "ancestorPart", + "part": "root", + "suffix": "[data-bf-state~=\"customizing\"]" + } + } + ] + }, + { + "id": "insights", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "view", + "attribute": "data-bf-view", + "values": [ + "list", + "report" + ] + } + ], + "states": [] + }, + { + "id": "pages", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "panel-view", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "profile", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [] + }, + { + "id": "session", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "workspace", + "continuityGroup": "session-workspace" + }, + { + "id": "main", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "session-workspace" + }, + { + "id": "chat", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "session-workspace" + }, + { + "id": "auxiliary", + "propertyProfile": "layout", + "visualRole": "panel", + "continuityGroup": "session-workspace" + }, + { + "id": "terminal", + "propertyProfile": "layout", + "visualRole": "panel", + "continuityGroup": "session-workspace" + } + ], + "facets": [], + "states": [ + { + "id": "dragging", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dragging\"]" + } + }, + { + "id": "terminalBottom", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"terminal-bottom\"]" + } + } + ] + }, + { + "id": "settings", + "parts": [ + { + "id": "root", + "propertyProfile": "layout", + "visualRole": "workspace", + "continuityGroup": "settings-workspace" + }, + { + "id": "content", + "propertyProfile": "layout", + "visualRole": "continuous-surface", + "continuityGroup": "settings-workspace" + }, + { + "id": "loading", + "propertyProfile": "overlay", + "visualRole": "content" + } + ], + "facets": [ + { + "id": "tab", + "attribute": "data-bf-tab", + "values": [ + "basics", + "appearance", + "models", + "archived-sessions", + "session-personalization", + "session-permissions", + "quick-actions", + "review", + "memories", + "mcp-tools", + "external-sources", + "acp-agents", + "editor", + "keyboard" + ] + } + ], + "states": [] + }, + { + "id": "skills", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "header", + "propertyProfile": "container" + }, + { + "id": "tabs", + "propertyProfile": "container" + }, + { + "id": "tab", + "propertyProfile": "container" + }, + { + "id": "tabDivider", + "propertyProfile": "container" + }, + { + "id": "content", + "propertyProfile": "container" + }, + { + "id": "installed", + "propertyProfile": "container" + }, + { + "id": "sidebar", + "propertyProfile": "container" + }, + { + "id": "sidebarHeader", + "propertyProfile": "container" + }, + { + "id": "sidebarTitle", + "propertyProfile": "container" + }, + { + "id": "sidebarNav", + "propertyProfile": "container" + }, + { + "id": "sidebarItem", + "propertyProfile": "container" + }, + { + "id": "sidebarItemIcon", + "propertyProfile": "container" + }, + { + "id": "sidebarItemLabel", + "propertyProfile": "container" + }, + { + "id": "sidebarItemCount", + "propertyProfile": "container" + }, + { + "id": "sidebarFooter", + "propertyProfile": "container" + }, + { + "id": "sidebarHint", + "propertyProfile": "container" + }, + { + "id": "main", + "propertyProfile": "container" + }, + { + "id": "toolbar", + "propertyProfile": "container" + }, + { + "id": "filterAction", + "propertyProfile": "container" + }, + { + "id": "addAction", + "propertyProfile": "container" + }, + { + "id": "loading", + "propertyProfile": "container" + }, + { + "id": "error", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + }, + { + "id": "list", + "propertyProfile": "container" + }, + { + "id": "skeleton", + "propertyProfile": "container" + }, + { + "id": "installedCard", + "propertyProfile": "container" + }, + { + "id": "installedCardTop", + "propertyProfile": "container" + }, + { + "id": "installedCardIcon", + "propertyProfile": "container" + }, + { + "id": "installedCardInfo", + "propertyProfile": "container" + }, + { + "id": "installedCardName", + "propertyProfile": "container" + }, + { + "id": "installedCardDescription", + "propertyProfile": "container" + }, + { + "id": "installedCardMeta", + "propertyProfile": "container" + }, + { + "id": "installedCardActions", + "propertyProfile": "container" + }, + { + "id": "installedCardDelete", + "propertyProfile": "container" + }, + { + "id": "pagination", + "propertyProfile": "container" + }, + { + "id": "pageButton", + "propertyProfile": "container" + }, + { + "id": "pageInfo", + "propertyProfile": "container" + }, + { + "id": "discover", + "propertyProfile": "container" + }, + { + "id": "discoverHero", + "propertyProfile": "container" + }, + { + "id": "discoverHeroContent", + "propertyProfile": "container" + }, + { + "id": "discoverTitle", + "propertyProfile": "container" + }, + { + "id": "discoverSubtitle", + "propertyProfile": "container" + }, + { + "id": "discoverSearch", + "propertyProfile": "container" + }, + { + "id": "discoverContent", + "propertyProfile": "container" + }, + { + "id": "resultsInfo", + "propertyProfile": "container" + }, + { + "id": "suite", + "propertyProfile": "container" + }, + { + "id": "suiteHero", + "propertyProfile": "container" + }, + { + "id": "suiteHeroCopy", + "propertyProfile": "container" + }, + { + "id": "suiteHeroActions", + "propertyProfile": "container" + }, + { + "id": "suiteModeToolbar", + "propertyProfile": "container" + }, + { + "id": "suiteModes", + "propertyProfile": "container" + }, + { + "id": "suiteModeTab", + "propertyProfile": "container" + }, + { + "id": "suiteModeTabLabel", + "propertyProfile": "container" + }, + { + "id": "suiteLoading", + "propertyProfile": "container" + }, + { + "id": "suiteSections", + "propertyProfile": "container" + }, + { + "id": "suiteSection", + "propertyProfile": "container" + }, + { + "id": "suiteSectionLabel", + "propertyProfile": "container" + }, + { + "id": "suiteGrid", + "propertyProfile": "container" + }, + { + "id": "suiteGroupCard", + "propertyProfile": "container" + }, + { + "id": "suiteGroupHead", + "propertyProfile": "container" + }, + { + "id": "suiteGroupTitleWrap", + "propertyProfile": "container" + }, + { + "id": "suiteGroupTitleRow", + "propertyProfile": "container" + }, + { + "id": "suiteGroupTitle", + "propertyProfile": "container" + }, + { + "id": "suiteGroupCount", + "propertyProfile": "container" + }, + { + "id": "suiteGroupActions", + "propertyProfile": "container" + }, + { + "id": "suiteSkills", + "propertyProfile": "container" + }, + { + "id": "suiteSkill", + "propertyProfile": "container" + }, + { + "id": "suiteSkillName", + "propertyProfile": "container" + }, + { + "id": "suiteSkillStatus", + "propertyProfile": "container" + } + ], + "facets": [ + { + "id": "tab", + "attribute": "data-bf-tab", + "values": [ + "installed", + "discover" + ] + }, + { + "id": "category", + "attribute": "data-bf-category", + "values": [ + "all", + "builtin", + "user", + "project", + "suite" + ] + }, + { + "id": "level", + "attribute": "data-bf-level", + "values": [ + "user", + "project" + ] + }, + { + "id": "mode", + "attribute": "data-bf-mode", + "values": [ + "agentic", + "Cowork", + "Claw", + "Team" + ] + } + ], + "states": [ + { + "id": "hover", + "selector": { + "kind": "self", + "suffix": ":hover" + } + }, + { + "id": "focusVisible", + "selector": { + "kind": "self", + "suffix": ":focus-visible" + } + }, + { + "id": "disabled", + "selector": { + "kind": "self", + "suffix": ":disabled" + } + }, + { + "id": "active", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"active\"]" + } + }, + { + "id": "empty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"empty\"]" + } + }, + { + "id": "shadowed", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"shadowed\"]" + } + }, + { + "id": "builtin", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"builtin\"]" + } + }, + { + "id": "enabled", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"enabled\"]" + } + }, + { + "id": "dirty", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"dirty\"]" + } + }, + { + "id": "covered", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"covered\"]" + } + } + ] + }, + { + "id": "terminal", + "parts": [ + { + "id": "root", + "propertyProfile": "container" + }, + { + "id": "empty", + "propertyProfile": "container" + } + ], + "facets": [], + "states": [ + { + "id": "inactive", + "selector": { + "kind": "self", + "suffix": "[data-bf-state~=\"inactive\"]" + } + } + ] + } + ], + "renderers": [ + "css-tokens", + "monaco", + "xterm", + "mermaid", + "generative-widget", + "bitfun-canvas" + ], + "defaultForceableProperties": [ + "backgroundColor", + "backgroundImage", + "backgroundSize", + "backgroundPosition", + "backgroundRepeat", + "backgroundImages", + "backgroundSizes", + "backgroundPositions", + "backgroundRepeats", + "backgroundBlendModes", + "color", + "caretColor", + "accentColor", + "textDecorationColor", + "borderColor", + "borderTopColor", + "borderRightColor", + "borderBottomColor", + "borderLeftColor", + "borderWidth", + "borderTopWidth", + "borderRightWidth", + "borderBottomWidth", + "borderLeftWidth", + "borderStyle", + "borderRadius", + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomRightRadius", + "borderBottomLeftRadius", + "outlineColor", + "outlineWidth", + "outlineOffset", + "outlineStyle", + "boxShadow", + "opacity", + "fontFamily", + "fontSize", + "fontWeight", + "fontStyle", + "fontVariantNumeric", + "lineHeight", + "letterSpacing", + "textAlign", + "verticalAlign", + "textIndent", + "textDecoration", + "textTransform", + "textOverflow", + "whiteSpace", + "wordBreak", + "overflowWrap", + "cursor", + "transition", + "transform", + "filter", + "backdropBlur", + "objectFit", + "objectPosition", + "mixBlendMode", + "isolation" + ], + "cssTokenNames": [ + "--bf-appearance-token-badge-padding-y", + "--bf-appearance-token-blur-base", + "--bf-appearance-token-blur-medium", + "--bf-appearance-token-blur-subtle", + "--bf-appearance-token-border-accent", + "--bf-appearance-token-border-accent-soft", + "--bf-appearance-token-border-accent-strong", + "--bf-appearance-token-border-accent-subtle", + "--bf-appearance-token-border-base", + "--bf-appearance-token-border-medium", + "--bf-appearance-token-border-prominent", + "--bf-appearance-token-border-strong", + "--bf-appearance-token-border-subtle", + "--bf-appearance-token-btn-ghost-color", + "--bf-appearance-token-btn-ghost-hover-bg", + "--bf-appearance-token-btn-ghost-hover-border", + "--bf-appearance-token-btn-ghost-hover-color", + "--bf-appearance-token-btn-primary-active-bg", + "--bf-appearance-token-btn-primary-active-border", + "--bf-appearance-token-btn-primary-active-color", + "--bf-appearance-token-btn-primary-active-shadow", + "--bf-appearance-token-btn-primary-active-transform", + "--bf-appearance-token-btn-primary-bg", + "--bf-appearance-token-btn-primary-border", + "--bf-appearance-token-btn-primary-color", + "--bf-appearance-token-btn-primary-hover-bg", + "--bf-appearance-token-btn-primary-hover-border", + "--bf-appearance-token-btn-primary-hover-color", + "--bf-appearance-token-btn-primary-hover-shadow", + "--bf-appearance-token-btn-primary-hover-transform", + "--bf-appearance-token-btn-primary-shadow", + "--bf-appearance-token-button-bg-hover", + "--bf-appearance-token-color-accent-100", + "--bf-appearance-token-color-accent-200", + "--bf-appearance-token-color-accent-300", + "--bf-appearance-token-color-accent-400", + "--bf-appearance-token-color-accent-50", + "--bf-appearance-token-color-accent-500", + "--bf-appearance-token-color-accent-500-rgb", + "--bf-appearance-token-color-accent-600", + "--bf-appearance-token-color-accent-700", + "--bf-appearance-token-color-bg-elevated", + "--bf-appearance-token-color-bg-primary", + "--bf-appearance-token-color-bg-scene", + "--bf-appearance-token-color-bg-secondary", + "--bf-appearance-token-color-bg-tertiary", + "--bf-appearance-token-color-bg-workbench", + "--bf-appearance-token-color-cyan-500", + "--bf-appearance-token-color-error", + "--bf-appearance-token-color-error-bg", + "--bf-appearance-token-color-error-border", + "--bf-appearance-token-color-indigo-500", + "--bf-appearance-token-color-info", + "--bf-appearance-token-color-info-bg", + "--bf-appearance-token-color-info-border", + "--bf-appearance-token-color-overlay-black-08", + "--bf-appearance-token-color-overlay-black-12", + "--bf-appearance-token-color-overlay-black-15", + "--bf-appearance-token-color-overlay-black-20", + "--bf-appearance-token-color-overlay-black-30", + "--bf-appearance-token-color-overlay-black-40", + "--bf-appearance-token-color-overlay-black-50", + "--bf-appearance-token-color-overlay-black-80", + "--bf-appearance-token-color-overlay-slate-04", + "--bf-appearance-token-color-overlay-slate-08", + "--bf-appearance-token-color-overlay-slate-12", + "--bf-appearance-token-color-overlay-slate-rgb", + "--bf-appearance-token-color-overlay-white-04", + "--bf-appearance-token-color-overlay-white-08", + "--bf-appearance-token-color-overlay-white-12", + "--bf-appearance-token-color-overlay-white-15", + "--bf-appearance-token-color-overlay-white-20", + "--bf-appearance-token-color-overlay-white-24", + "--bf-appearance-token-color-overlay-white-60", + "--bf-appearance-token-color-overlay-white-70", + "--bf-appearance-token-color-overlay-white-80", + "--bf-appearance-token-color-overlay-white-90", + "--bf-appearance-token-color-overlay-white-95", + "--bf-appearance-token-color-purple-100", + "--bf-appearance-token-color-purple-200", + "--bf-appearance-token-color-purple-500", + "--bf-appearance-token-color-purple-600", + "--bf-appearance-token-color-static-black", + "--bf-appearance-token-color-static-black-rgb", + "--bf-appearance-token-color-static-white", + "--bf-appearance-token-color-static-white-rgb", + "--bf-appearance-token-color-success", + "--bf-appearance-token-color-success-bg", + "--bf-appearance-token-color-success-border", + "--bf-appearance-token-color-text-disabled", + "--bf-appearance-token-color-text-muted", + "--bf-appearance-token-color-text-primary", + "--bf-appearance-token-color-text-secondary", + "--bf-appearance-token-color-warning", + "--bf-appearance-token-color-warning-bg", + "--bf-appearance-token-color-warning-border", + "--bf-appearance-token-domain-capability-creative", + "--bf-appearance-token-domain-capability-docs", + "--bf-appearance-token-domain-capability-ops", + "--bf-appearance-token-domain-capability-testing", + "--bf-appearance-token-domain-context-compression", + "--bf-appearance-token-domain-generative-ui", + "--bf-appearance-token-domain-git-lane-0", + "--bf-appearance-token-domain-git-lane-1", + "--bf-appearance-token-domain-git-lane-2", + "--bf-appearance-token-domain-git-lane-3", + "--bf-appearance-token-domain-git-lane-4", + "--bf-appearance-token-domain-git-lane-5", + "--bf-appearance-token-domain-git-lane-6", + "--bf-appearance-token-domain-git-lane-7", + "--bf-appearance-token-domain-insights-issue", + "--bf-appearance-token-domain-insights-neutral", + "--bf-appearance-token-domain-insights-positive", + "--bf-appearance-token-domain-insights-time", + "--bf-appearance-token-domain-inspector-active-background", + "--bf-appearance-token-domain-inspector-active-border", + "--bf-appearance-token-domain-inspector-active-border-subtle", + "--bf-appearance-token-domain-inspector-browser-tooltip-background", + "--bf-appearance-token-domain-inspector-main-tooltip-background", + "--bf-appearance-token-domain-inspector-selected-background", + "--bf-appearance-token-domain-inspector-selected-border", + "--bf-appearance-token-domain-inspector-tooltip-shadow", + "--bf-appearance-token-domain-inspector-tooltip-text", + "--bf-appearance-token-domain-mermaid-diagram", + "--bf-appearance-token-domain-mini-app", + "--bf-appearance-token-domain-progress-compacting", + "--bf-appearance-token-domain-review-judge", + "--bf-appearance-token-domain-review-member-default", + "--bf-appearance-token-domain-review-worker", + "--bf-appearance-token-domain-teal-action", + "--bf-appearance-token-domain-template-memories", + "--bf-appearance-token-domain-text-stroke-0", + "--bf-appearance-token-domain-text-stroke-1", + "--bf-appearance-token-domain-text-stroke-2", + "--bf-appearance-token-domain-text-stroke-3", + "--bf-appearance-token-domain-text-stroke-4", + "--bf-appearance-token-domain-todo", + "--bf-appearance-token-domain-tool-assistant-action", + "--bf-appearance-token-domain-tool-git", + "--bf-appearance-token-domain-tool-mcp", + "--bf-appearance-token-domain-tool-review-summary", + "--bf-appearance-token-domain-tool-search", + "--bf-appearance-token-domain-tool-terminal", + "--bf-appearance-token-domain-tool-web-search", + "--bf-appearance-token-easing-accelerate", + "--bf-appearance-token-easing-decelerate", + "--bf-appearance-token-easing-smooth", + "--bf-appearance-token-easing-standard", + "--bf-appearance-token-element-bg-base", + "--bf-appearance-token-element-bg-hover", + "--bf-appearance-token-element-bg-medium", + "--bf-appearance-token-element-bg-soft", + "--bf-appearance-token-element-bg-strong", + "--bf-appearance-token-element-bg-subtle", + "--bf-appearance-token-flowchat-card-expanded-pad-x", + "--bf-appearance-token-flowchat-card-expanded-pad-y", + "--bf-appearance-token-flowchat-card-gap", + "--bf-appearance-token-flowchat-card-pad-x", + "--bf-appearance-token-flowchat-card-pad-y", + "--bf-appearance-token-flowchat-card-radius", + "--bf-appearance-token-flowchat-code-block-pad-x", + "--bf-appearance-token-flowchat-code-block-pad-y", + "--bf-appearance-token-flowchat-code-font-size", + "--bf-appearance-token-flowchat-code-line-height", + "--bf-appearance-token-flowchat-compact-line-height", + "--bf-appearance-token-flowchat-content-inline-pad", + "--bf-appearance-token-flowchat-content-inline-pad-mobile", + "--bf-appearance-token-flowchat-control-gap", + "--bf-appearance-token-flowchat-control-pad-x", + "--bf-appearance-token-flowchat-control-pad-y", + "--bf-appearance-token-flowchat-flow-item-gap", + "--bf-appearance-token-flowchat-font-size-2xl", + "--bf-appearance-token-flowchat-font-size-2xs", + "--bf-appearance-token-flowchat-font-size-3xl", + "--bf-appearance-token-flowchat-font-size-base", + "--bf-appearance-token-flowchat-font-size-lg", + "--bf-appearance-token-flowchat-font-size-sm", + "--bf-appearance-token-flowchat-font-size-xl", + "--bf-appearance-token-flowchat-font-size-xs", + "--bf-appearance-token-flowchat-font-size-xxs", + "--bf-appearance-token-flowchat-inline-gap", + "--bf-appearance-token-flowchat-link-color", + "--bf-appearance-token-flowchat-link-hover-color", + "--bf-appearance-token-flowchat-support-line-height", + "--bf-appearance-token-flowchat-text-line-height", + "--bf-appearance-token-flowchat-turn-gap", + "--bf-appearance-token-font-family-mono", + "--bf-appearance-token-font-family-sans", + "--bf-appearance-token-font-size-2xl", + "--bf-appearance-token-font-size-3xl", + "--bf-appearance-token-font-size-4xl", + "--bf-appearance-token-font-size-base", + "--bf-appearance-token-font-size-lg", + "--bf-appearance-token-font-size-sm", + "--bf-appearance-token-font-size-xl", + "--bf-appearance-token-font-size-xs", + "--bf-appearance-token-font-weight-bold", + "--bf-appearance-token-font-weight-medium", + "--bf-appearance-token-font-weight-normal", + "--bf-appearance-token-font-weight-semibold", + "--bf-appearance-token-git-color-added", + "--bf-appearance-token-git-color-branch", + "--bf-appearance-token-git-color-branch-bg", + "--bf-appearance-token-git-color-branch-bg-hover", + "--bf-appearance-token-git-color-changes", + "--bf-appearance-token-git-color-deleted", + "--bf-appearance-token-git-color-staged", + "--bf-appearance-token-glass-bg-active", + "--bf-appearance-token-glass-bg-base", + "--bf-appearance-token-glass-bg-hover", + "--bf-appearance-token-glass-blue-subtle", + "--bf-appearance-token-glass-border-base", + "--bf-appearance-token-glass-border-hover", + "--bf-appearance-token-glass-red-base", + "--bf-appearance-token-glass-red-hover", + "--bf-appearance-token-glass-red-subtle", + "--bf-appearance-token-glow-shadow-blue", + "--bf-appearance-token-inner-glow-top", + "--bf-appearance-token-inner-glow-top-hover", + "--bf-appearance-token-language-blue", + "--bf-appearance-token-language-cyan", + "--bf-appearance-token-language-green", + "--bf-appearance-token-language-orange", + "--bf-appearance-token-language-purple", + "--bf-appearance-token-language-red", + "--bf-appearance-token-language-slate", + "--bf-appearance-token-language-yellow", + "--bf-appearance-token-line-height-base", + "--bf-appearance-token-line-height-relaxed", + "--bf-appearance-token-line-height-tight", + "--bf-appearance-token-motion-base", + "--bf-appearance-token-motion-fast", + "--bf-appearance-token-motion-instant", + "--bf-appearance-token-motion-lazy", + "--bf-appearance-token-motion-slow", + "--bf-appearance-token-opacity-disabled", + "--bf-appearance-token-opacity-hover", + "--bf-appearance-token-prism-dark-comment", + "--bf-appearance-token-prism-dark-foreground", + "--bf-appearance-token-prism-dark-function", + "--bf-appearance-token-prism-dark-keyword", + "--bf-appearance-token-prism-dark-number", + "--bf-appearance-token-prism-dark-property", + "--bf-appearance-token-prism-dark-punctuation", + "--bf-appearance-token-prism-dark-string", + "--bf-appearance-token-prism-dark-tag", + "--bf-appearance-token-prism-light-comment", + "--bf-appearance-token-prism-light-foreground", + "--bf-appearance-token-prism-light-function", + "--bf-appearance-token-prism-light-keyword", + "--bf-appearance-token-prism-light-number", + "--bf-appearance-token-prism-light-property", + "--bf-appearance-token-prism-light-punctuation", + "--bf-appearance-token-prism-light-string", + "--bf-appearance-token-prism-light-tag", + "--bf-appearance-token-scene-viewport-border-width", + "--bf-appearance-token-scrollbar-thumb", + "--bf-appearance-token-scrollbar-thumb-hover", + "--bf-appearance-token-shadow-base", + "--bf-appearance-token-shadow-lg", + "--bf-appearance-token-shadow-sm", + "--bf-appearance-token-shadow-xl", + "--bf-appearance-token-shadow-xs", + "--bf-appearance-token-size-gap-1", + "--bf-appearance-token-size-gap-10", + "--bf-appearance-token-size-gap-12", + "--bf-appearance-token-size-gap-16", + "--bf-appearance-token-size-gap-2", + "--bf-appearance-token-size-gap-3", + "--bf-appearance-token-size-gap-4", + "--bf-appearance-token-size-gap-5", + "--bf-appearance-token-size-gap-6", + "--bf-appearance-token-size-gap-8", + "--bf-appearance-token-size-radius-2xl", + "--bf-appearance-token-size-radius-base", + "--bf-appearance-token-size-radius-full", + "--bf-appearance-token-size-radius-lg", + "--bf-appearance-token-size-radius-md", + "--bf-appearance-token-size-radius-sm", + "--bf-appearance-token-size-radius-xl", + "--bf-appearance-token-tool-card-action-font-size", + "--bf-appearance-token-tool-card-action-line-height", + "--bf-appearance-token-tool-card-expanded-pad-x", + "--bf-appearance-token-tool-card-font-mono", + "--bf-appearance-token-tool-card-header-icon-slot", + "--bf-appearance-token-tool-card-header-pad-right", + "--bf-appearance-token-tool-card-header-pad-y" + ], + "widgetVariableNames": [ + "--bf-appearance-token-color-bg-primary", + "--bf-appearance-token-color-static-white", + "--bf-appearance-token-color-static-black", + "--bf-appearance-token-color-bg-secondary", + "--bf-appearance-token-color-bg-tertiary", + "--bf-appearance-token-color-bg-elevated", + "--bf-appearance-token-color-bg-scene", + "--bf-appearance-token-color-text-primary", + "--bf-appearance-token-color-text-secondary", + "--bf-appearance-token-color-text-muted", + "--bf-appearance-token-color-text-disabled", + "--bf-appearance-token-color-accent-500", + "--bf-appearance-token-color-accent-500-rgb", + "--bf-appearance-token-color-accent-600", + "--bf-appearance-token-color-success", + "--bf-appearance-token-color-success-bg", + "--bf-appearance-token-color-warning", + "--bf-appearance-token-color-warning-bg", + "--bf-appearance-token-color-error", + "--bf-appearance-token-color-error-bg", + "--bf-appearance-token-color-info", + "--bf-appearance-token-color-info-bg", + "--bf-appearance-token-color-info-border", + "--bf-appearance-token-border-subtle", + "--bf-appearance-token-border-base", + "--bf-appearance-token-border-medium", + "--bf-appearance-token-element-bg-subtle", + "--bf-appearance-token-element-bg-soft", + "--bf-appearance-token-element-bg-base", + "--bf-appearance-token-element-bg-medium", + "--bf-appearance-token-element-bg-hover", + "--bf-appearance-token-shadow-xs", + "--bf-appearance-token-shadow-sm", + "--bf-appearance-token-motion-fast", + "--bf-appearance-token-motion-base", + "--bf-appearance-token-easing-standard", + "--bf-appearance-token-font-family-sans", + "--bf-appearance-token-font-family-mono", + "--bf-appearance-token-btn-primary-bg", + "--bf-appearance-token-btn-primary-color", + "--bf-appearance-token-btn-primary-border", + "--bf-appearance-token-btn-primary-shadow", + "--bf-appearance-token-btn-primary-hover-bg", + "--bf-appearance-token-btn-primary-hover-color", + "--bf-appearance-token-btn-primary-hover-border", + "--bf-appearance-token-btn-primary-hover-shadow", + "--bf-appearance-token-btn-primary-hover-transform", + "--bf-appearance-token-btn-primary-active-bg", + "--bf-appearance-token-btn-primary-active-color", + "--bf-appearance-token-btn-primary-active-border", + "--bf-appearance-token-btn-primary-active-shadow", + "--bf-appearance-token-btn-primary-active-transform", + "--bf-appearance-token-btn-ghost-color", + "--bf-appearance-token-btn-ghost-hover-bg", + "--bf-appearance-token-btn-ghost-hover-color", + "--bf-appearance-token-btn-ghost-hover-border" + ] +} diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/authoring-workflow.md b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/authoring-workflow.md new file mode 100644 index 0000000000..8304f34db6 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/authoring-workflow.md @@ -0,0 +1,52 @@ +# Appearance authoring workflow + +Follow this workflow for any style. A style example may refine design decisions but must not replace these contract checks. + +## 1. Define the intended delta + +Identify the requested base mode, surfaces, renderers, assets, motion behavior, and accessibility requirements. Prefer a sparse overlay. Leave an owner unchanged when the built-in Appearance already satisfies the design. + +Separate four kinds of decisions: + +- Contract facts: registered surfaces, Parts, states, facets, contexts, properties, tokens, and limits. +- Style choices: color, opacity, material, imagery, crop, emphasis, and coverage. +- Build facts: inputs, hashes, generated assets, archive, and host revision. +- Runtime evidence: imported screenshots or observations across required states. + +## 2. Query before authoring + +Use `contract show` for every component or scene being changed. Confirm each Part's property profile and allowed properties. Query renderer tokens before adding CSS or Widget values. + +Do not use an example's surface plan to infer current host capabilities. The registry is the authority; the example only records a previously validated selection. + +## 3. Initialize and author sparsely + +Run `bitfun_appearance.py init`, then add only required globals, materials, component Parts, scenes, renderers, and assets. + +Use typed Style IR values. Compose Parts through `materials: []`; the singular `material` field is unsupported. Keep image references in Style IR image-only and put video only in top-level `backgroundMedia`. + +## 4. Inspect assets before packaging + +For source artwork, inspect contact sheets or the original image before selecting crops. Generate role-specific previews when one source is reused across different aspect ratios. Check bright and calm frames when a background moves. + +Use the default adaptive policy in [media-quality-policy.md](media-quality-policy.md) unless the user explicitly requests a fixed quality mode. Preserve both the requested policy and the resolved encoding in the build record. + +Do not infer visual quality from successful encoding. Verify subject placement, text readability, contrast, and reduced-motion fallback separately. + +## 5. Validate in layers + +Run project validation, build the archive, and validate the archive. Resolve errors by manifest path and issue code. Treat warnings as design or continuity debt and require zero host warnings for finished examples. + +When a checkout is available, first check registry synchronization and then run strict host verification. Do not update an example's validated revision until both pass against a clean checkout. + +## 6. Record reproducibility + +For generated skins, record source paths and hashes, style input hashes, build parameters, output hashes, registry provenance, host verification, and runtime inspection state. Rebuild from the record and provide a read-only drift check. + +Keep reports, source files, and contact sheets outside the importable `package/` directory. + +## 7. Inspect at runtime + +Import the archive into BitFun and inspect every intentionally styled owner plus representative unchanged owners. Include detached windows, long content, dialogs, renderer surfaces, interaction states, reduced motion, and bright/dark background frames where relevant. + +Compiler validation and runtime visual inspection are separate completion conditions. diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/media-quality-policy.md b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/media-quality-policy.md new file mode 100644 index 0000000000..1c2de79aba --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/media-quality-policy.md @@ -0,0 +1,43 @@ +# Media quality policy + +Preserve the user's source file. Generated package assets are derivatives selected to satisfy BitFun package limits and runtime compatibility. + +## Default video policy + +Use `auto` unless the user explicitly requests another policy. + +`auto` selects the highest quality that satisfies the 64 MiB background-video limit: + +1. For sources at or below 12 million pixel-seconds after the 60-second cap, try VP9 codec-lossless encoding first. +2. For larger sources, or when the lossless attempt exceeds the limit, start at CRF 20. +3. When an attempt exceeds 64 MiB, increase CRF according to the measured size ratio and retry. +4. Stop at the first valid output and record every attempt plus the selected result. + +The host transform still caps duration at 60 seconds, bounds landscape output to 3840x2160 and portrait output to 2160x3840, normalizes playback to 30 FPS, converts to `yuv420p`, removes audio, and strips metadata. It converts non-square source pixels to `SAR 1:1` while preserving the source display aspect ratio, so browser `videoWidth` and `videoHeight` remain inside the host limits. + +Treat the browser display dimensions as authoritative. FFprobe's coded `width` and `height` may look valid while a non-1:1 sample aspect ratio expands the displayed video beyond 4,096 pixels per side or 9 million pixels. Generated videos and extracted still frames must both normalize the sample aspect ratio. + +`codecLossless: true` means VP9 does not add quantization loss after that transform. It does not mean the packaged derivative is byte-for-byte or pixel-for-pixel identical to the original source. + +Available policies: + +- `auto`: adaptive highest quality; default. +- `lossless`: require codec-lossless VP9 and fail when it exceeds 64 MiB. +- `high`: use CRF 20 and fail when it exceeds the limit. +- `balanced`: use CRF 32 and fail when it exceeds the limit. +- explicit `--video-crf`: override the policy for reproducibility or manual tuning. + +Do not silently fall back from an explicitly requested fixed policy. Report the size failure so the user can choose a different policy. + +## Default static policy + +Use `auto` for generated WebP assets: + +1. Try lossless WebP. +2. Keep it when it satisfies the asset limit. +3. Otherwise try lossy qualities 95, 92, 88, 84, 80, 75, and 70. +4. Stop at the highest quality that satisfies the limit. + +Ordinary images are limited to 16 MiB. The package preview is limited to 4 MiB. Explicit lossless or quality modes fail instead of silently changing the user's requested policy. + +Record the selected mode, quality, file size, and prior attempts in the build record. Runtime visual inspection remains required even when lossless encoding succeeds because crop, tint, blur, scaling, and moving backgrounds affect readability independently of codec quality. diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/package-contract.md b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/package-contract.md new file mode 100644 index 0000000000..c8c9898060 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/package-contract.md @@ -0,0 +1,130 @@ +# BitFun Appearance package contract + +## Package shape + +- Archive extension: `.bitfun-appearance` +- Archive format: ZIP +- Root manifest: `appearance.json` +- `schema`: `bitfun.appearance` +- `schemaVersion`: `1` +- Image formats: PNG, JPEG, WebP, or GIF +- Background video formats: MP4 or WebM +- Imported packages are sparse overlays merged with the built-in light or dark Appearance package. + +The package is declarative. It cannot carry CSS, selectors, markup, scripts, SVG, fonts, external URLs, or executable content. Video is accepted only through top-level `backgroundMedia` and never through component, scene, or material Style IR. + +## Top-level model + +An Appearance manifest may define: + +- package identity and mode; +- an explicit package preview asset reference; +- an optional host-managed background video and poster; +- required capabilities; +- typed global values; +- reusable materials; +- registered component definitions; +- registered scene definitions; +- renderer settings; +- declared package assets; +- SHA-256 integrity hashes. + +`preview` uses `{ "kind": "asset", "assetId": "..." }` and must reference a declared image asset when present. + +`backgroundMedia` uses this closed shape: + +```json +{ + "kind": "video", + "assetId": "background-video", + "posterAssetId": "background-poster", + "fit": "cover", + "position": "center" +} +``` + +The video asset must be `video/mp4` or `video/webm`; the poster must be an image. Declare `background-media.v1`. Playback is host-owned: muted, looping, non-interactive, paused while hidden, and replaced by the poster for reduced-motion users. + +Use `scripts/bitfun_appearance.py init` for a valid starting structure. Use its `contract` commands for the exact standalone registry. + +## Style IR + +Values are typed records, not raw CSS. Common kinds include: + +- colors: `hex`, `rgb`, `transparent`, `ref`; +- lengths: `px`, `percent`, `zero`, `lengthKeyword`, `ref`; +- assets: `asset` with `assetId`; +- compound values: shadow, transform, background size, background position. + +Materials are definitions with `style` and an optional `visualRole`. Scene and component Part rules compose one to eight material ids through `materials: string[]`, then add `base`, `facets`, `states`, and `contexts` styling. The singular `material` field is unsupported. + +```json +{ + "materials": { + "quiet-card": { + "visualRole": "card", + "style": { + "backgroundColor": { "kind": "ref", "path": "globals.colors.surface" } + } + } + }, + "components": { + "skill-card": { + "parts": { + "root": { + "materials": ["quiet-card"], + "decorationIntent": "framed" + } + } + } + } +} +``` + +`decorationIntent` accepts `flat`, `separator`, or `framed`. + +Part descriptors may define `propertyProfile`, `allowedProperties`, `forceableProperties`, `visualRole`, and `continuityGroup`. Query the registry instead of assuming every Part accepts every Style IR property. `cascade: "override"` only makes descriptor-approved forceable paint properties important. + +Only registered properties are accepted. Part descriptors may further narrow `allowedProperties`. + +## Background layers + +For multiple layers, provide arrays of equal length: + +- `backgroundImages` +- `backgroundSizes` +- `backgroundPositions` +- `backgroundRepeats` +- `backgroundBlendModes` + +The first image is painted topmost. + +## Limits + +- compressed archive: 96 MiB; +- expanded content: 128 MiB; +- manifest: 256 KiB; +- one image: 16 MiB; +- one background video: 64 MiB; +- preview image: 4 MiB; +- files: 64 maximum; +- image side: 16,384 pixels maximum; +- image pixels: 50 million maximum. + +The production host additionally limits video to 60 seconds, 4,096 displayed pixels per side, and 9 million displayed pixels, and verifies that the current WebView can decode the container and codec. Pixel aspect ratio affects the displayed dimensions reported by the browser; a coded 3840x2160 stream can still exceed the limit when its sample aspect ratio is not 1:1. The standalone Python validator verifies structure, file size, and container signature; `verify_host.py` additionally probes packaged video display dimensions, while final import remains the codec compatibility check. + +The package rejects unsafe ZIP paths, symlinks, undeclared files, unsupported media, invalid references, and integrity mismatches. + +## Registry provenance + +`appearance-registry.json` is a self-contained production registry snapshot. Consumers of this skill do not need BitFun source code for offline authoring. Query the snapshot through the script instead of editing it by hand. When a checkout is available, run `python scripts/sync_registry.py --check` before relying on the snapshot. + +Maintainers synchronize it with `scripts/sync_registry.py`. Provenance includes the source revision, dirty state, tree evidence hash, and generation timestamp; a matching commit hash alone does not prove that a dirty working tree is synchronized. + +Published snapshots must come from a clean checkout. `--allow-dirty` exists only for local investigation. + +## Validation diagnostics + +The standalone validator and production host report stable issue codes and manifest paths. The standalone validator also reports non-blocking normalization, continuity, and cascade warnings. The production host additionally attaches component or scene context; unknown-Part issues include the surface's currently allowed Part ids. Product UI groups these issues by surface or package section instead of flattening them into one message. + +When import fails, use the reported path and allowed-Part list to update the manifest, then confirm the current descriptor with `contract show`. Do not rename or map an unknown Part based on another package. diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/renderer-contracts.md b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/renderer-contracts.md new file mode 100644 index 0000000000..a1eec2675a --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/renderer-contracts.md @@ -0,0 +1,54 @@ +# Renderer contracts + +List registered renderer adapters with: + +```powershell +python scripts/bitfun_appearance.py contract list renderers +``` + +Renderer entries use this shape: + +```json +{ + "renderers": { + "": { + "version": 1, + "settings": {} + } + } +} +``` + +## css-tokens + +`settings` contains `tokens` and `background`. Query accepted token names with: + +```powershell +python scripts/bitfun_appearance.py contract tokens css +``` + +## monaco + +Accepted settings: `id`, `base`, `inherit`, `rules`, and `colors`. IDs use lowercase letters, digits, and hyphens. Supported bases are `vs`, `vs-dark`, `hc-black`, and `hc-light`. + +## xterm + +Accepted settings: `surfaces`, `fontWeight`, and `fontWeightBold`. `surfaces` may define `terminal` and `output` color maps. + +## mermaid + +Accepted settings: `mode` and `palette`. The validator reports unsupported or missing palette fields. + +## generative-widget + +Accepted settings: `id`, `mode`, and `vars`. Query accepted variable names with: + +```powershell +python scripts/bitfun_appearance.py contract tokens widget +``` + +## bitfun-canvas + +Accepted settings: `id`, `mode`, `bg`, `panel`, `fg`, `muted`, `border`, `accent`, `success`, `warning`, `danger`, and `info`. + +Run package validation for the exact value constraints and required fields. diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/style-example-contract.md b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/style-example-contract.md new file mode 100644 index 0000000000..3d0fb09544 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/references/style-example-contract.md @@ -0,0 +1,75 @@ +# Style example contract + +A style example teaches the model how one visual direction chooses to use the parent Appearance contract. It is not a registry, full-surface baseline, or mandatory template. + +## Required contents + +Each example contains: + +```text +/ + SKILL.md + references/ + style-playbook.md + surface-plan.json + default-palette.json # when the style uses a fixed semantic palette + palette-contract.md # when palette validation is non-trivial + scripts/ # only when deterministic style-specific generation is useful +``` + +Do not add README, changelog, installation, or process-history files to the Skill example. + +## SKILL.md responsibilities + +State: + +- the visual goal and suitable source material; +- the required parent workflow and references; +- the style's palette and material strategy; +- which surfaces are emphasized and which remain default; +- asset roles and crop rules, when applicable; +- style-specific build commands; +- runtime risks and inspection requirements; +- the fact that the registry remains authoritative. + +Do not repeat the full package contract, validator documentation, registry maintenance workflow, or generic media implementation. + +## Surface plan requirements + +Use this metadata: + +```json +{ + "schema": "bitfun.appearance.style-surface-plan", + "schemaVersion": 1, + "scope": "example-style-selection", + "styleId": "example-id", + "validatedAgainstRegistryRevision": "", + "scenes": {}, + "components": {} +} +``` + +The plan must be sparse and must represent deliberate style coverage. Validate every selected Part and property against the bundled registry. Never describe the plan as the current Appearance surface, and never copy it wholesale into another style. + +## Script ownership + +Import generic lifecycle helpers from `scripts/build_support.py` and generic media operations from `scripts/media_support.py`. + +Keep these inside the example: + +- semantic palette interpretation; +- materials and renderer aesthetics; +- image roles and role-specific transforms; +- crop defaults; +- surface selection; +- style-specific runtime checks; +- the thin command wrapper that combines those choices. + +Use only the current command path and current record schema. Records must identify the recipe that produced them. + +## Golden outputs + +Importable example skins live outside this Skill. Link them conceptually through machine-readable metadata rather than copying their packages or source media into the Skill. + +Golden outputs prove that a recipe can produce a real package. They do not turn one work's colors, crops, or component coverage into reusable rules. diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/bitfun_appearance.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/bitfun_appearance.py new file mode 100644 index 0000000000..06baa10d1f --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/bitfun_appearance.py @@ -0,0 +1,1786 @@ +#!/usr/bin/env python3 +"""Build, validate, inspect, and query BitFun Appearance packages.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import sys +import zipfile +from pathlib import Path, PurePosixPath +from typing import Any, Callable, Iterable, Mapping, Sequence + + +SCHEMA = "bitfun.appearance" +SCHEMA_VERSION = 1 +REGISTRY_PATH = Path(__file__).resolve().parent.parent / "references" / "appearance-registry.json" +MANIFEST_NAME = "appearance.json" +MAX_ARCHIVE_BYTES = 96 * 1024 * 1024 +MAX_EXPANDED_BYTES = 128 * 1024 * 1024 +MAX_MANIFEST_BYTES = 256 * 1024 +MAX_IMAGE_BYTES = 16 * 1024 * 1024 +MAX_VIDEO_BYTES = 64 * 1024 * 1024 +MAX_PREVIEW_BYTES = 4 * 1024 * 1024 +MAX_ENTRIES = 64 +MAX_DIMENSION = 16_384 +MAX_PIXELS = 50_000_000 + +ID_PATTERN = re.compile(r"^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$") +VERSION_PATTERN = re.compile(r"^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$") +REFERENCE_PATTERN = re.compile( + r"^globals\.(colors|lengths|numbers|durations|easings|fontFamilies|shadows)\.[a-z][a-zA-Z0-9.-]*$" +) +ASSET_PATH_PATTERN = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9_./-]*$") +FORBIDDEN_RENDERER_TEXT = re.compile(r"(?:https?://|javascript:|data:|url\s*\(|])", re.I) +WIDGET_SAFE_VALUE = re.compile(r"^(?!.*(?:https?://|javascript:|data:|url\s*\(|[{};@\\]))[\w\s#.,%()'\"+\-/*]+$", re.I) + +STYLE_PROPERTIES = { + "backgroundColor", "backgroundImage", "color", "caretColor", "accentColor", + "textDecorationColor", "borderColor", "borderTopColor", "backgroundSize", + "backgroundPosition", "backgroundRepeat", "backgroundImages", "backgroundSizes", + "backgroundPositions", "backgroundRepeats", "backgroundBlendModes", + "borderRightColor", "borderBottomColor", "borderLeftColor", "borderWidth", + "borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth", + "borderStyle", "borderRadius", "borderTopLeftRadius", "borderTopRightRadius", + "borderBottomRightRadius", "borderBottomLeftRadius", "boxSizing", "outlineColor", + "outlineWidth", "outlineOffset", "outlineStyle", "boxShadow", "opacity", + "fontFamily", "fontSize", "fontWeight", "lineHeight", "fontStyle", + "fontVariantNumeric", "letterSpacing", "textAlign", "verticalAlign", "textIndent", + "textDecoration", "textTransform", "textOverflow", "whiteSpace", "wordBreak", + "overflowWrap", "display", "flexDirection", "flexWrap", "flexGrow", "flexShrink", + "flexBasis", "alignItems", "alignContent", "alignSelf", "justifyContent", + "justifySelf", "justifyItems", "placeItems", "placeContent", "order", + "gridTemplateColumns", "gridTemplateRows", "gridAutoFlow", "gridColumnSpan", + "gridRowSpan", "gap", "rowGap", "columnGap", "width", "minWidth", "maxWidth", + "height", "minHeight", "maxHeight", "padding", "paddingBlock", "paddingInline", + "paddingTop", "paddingRight", "paddingBottom", "paddingLeft", "margin", + "marginBlock", "marginInline", "marginTop", "marginRight", "marginBottom", + "marginLeft", "position", "insetBlock", "insetInline", "top", "right", "bottom", + "left", "zIndex", "aspectRatio", "overflow", "overflowX", "overflowY", "cursor", + "transition", "transform", "filter", "backdropBlur", "objectFit", "objectPosition", + "mixBlendMode", "isolation", +} + +PAINT_PROPERTIES = { + "backgroundColor", "backgroundImage", "backgroundSize", "backgroundPosition", + "backgroundRepeat", "backgroundImages", "backgroundSizes", "backgroundPositions", + "backgroundRepeats", "backgroundBlendModes", "color", "caretColor", "accentColor", + "textDecorationColor", "borderColor", "borderTopColor", "borderRightColor", + "borderBottomColor", "borderLeftColor", "borderWidth", "borderTopWidth", + "borderRightWidth", "borderBottomWidth", "borderLeftWidth", "borderStyle", + "borderRadius", "borderTopLeftRadius", "borderTopRightRadius", + "borderBottomRightRadius", "borderBottomLeftRadius", "outlineColor", "outlineWidth", + "outlineOffset", "outlineStyle", "boxShadow", "opacity", "fontFamily", "fontSize", + "fontWeight", "fontStyle", "fontVariantNumeric", "lineHeight", "letterSpacing", + "textAlign", "verticalAlign", "textIndent", "textDecoration", "textTransform", + "textOverflow", "whiteSpace", "wordBreak", "overflowWrap", "cursor", "transition", + "transform", "filter", "backdropBlur", "objectFit", "objectPosition", + "mixBlendMode", "isolation", +} +CONTROL_PROPERTIES = PAINT_PROPERTIES | { + "display", "flexDirection", "flexWrap", "alignItems", "alignContent", + "justifyContent", "boxSizing", "width", "minWidth", "maxWidth", "height", + "minHeight", "maxHeight", "padding", "paddingBlock", "paddingInline", "paddingTop", + "paddingRight", "paddingBottom", "paddingLeft", "gap", "rowGap", "columnGap", + "flexGrow", "flexShrink", "flexBasis", "alignSelf", "aspectRatio", +} +CONTAINER_PROPERTIES = CONTROL_PROPERTIES | { + "justifySelf", "justifyItems", "placeItems", "placeContent", "gridTemplateColumns", + "gridTemplateRows", "gridAutoFlow", "gridColumnSpan", "gridRowSpan", "margin", + "marginBlock", "marginInline", "marginTop", "marginRight", "marginBottom", + "marginLeft", "overflow", "overflowX", "overflowY", +} +LAYOUT_PROPERTIES = CONTAINER_PROPERTIES | { + "order", "position", "insetBlock", "insetInline", "top", "right", "bottom", + "left", "zIndex", +} +PROPERTY_PROFILES = { + "paint": PAINT_PROPERTIES, + "control": CONTROL_PROPERTIES, + "container": CONTAINER_PROPERTIES, + "layout": LAYOUT_PROPERTIES, + "overlay": LAYOUT_PROPERTIES, +} +VISUAL_ROLES = { + "workspace", "continuous-surface", "panel", "toolbar", "card", "control", + "popup", "dialog", "divider", "content", "decoration", +} + +COLOR_PROPERTIES = { + "backgroundColor", "color", "caretColor", "accentColor", "textDecorationColor", + "borderColor", "borderTopColor", "borderRightColor", "borderBottomColor", + "borderLeftColor", "outlineColor", +} +LENGTH_PROPERTIES = { + "borderWidth", "borderTopWidth", "borderRightWidth", "borderBottomWidth", + "borderLeftWidth", "borderRadius", "outlineWidth", "outlineOffset", "fontSize", + "borderTopLeftRadius", "borderTopRightRadius", "borderBottomRightRadius", + "borderBottomLeftRadius", "letterSpacing", "textIndent", "gap", "rowGap", + "columnGap", "width", "minWidth", "maxWidth", "height", "minHeight", "maxHeight", + "padding", "paddingBlock", "paddingInline", "paddingTop", "paddingRight", + "paddingBottom", "paddingLeft", "margin", "marginBlock", "marginInline", "marginTop", + "marginRight", "marginBottom", "marginLeft", "flexBasis", "insetBlock", "insetInline", + "top", "right", "bottom", "left", "backdropBlur", +} +NUMBER_PROPERTIES = { + "opacity", "fontWeight", "lineHeight", "flexGrow", "flexShrink", "gridColumnSpan", + "gridRowSpan", "zIndex", "order", +} + +ENUM_VALUES: dict[str, set[str]] = { + "borderStyle": {"none", "solid", "dashed", "dotted"}, + "outlineStyle": {"none", "solid", "dashed", "dotted"}, + "backgroundSize": {"auto", "cover", "contain"}, + "backgroundPosition": {"center", "top", "right", "bottom", "left"}, + "backgroundRepeat": {"repeat", "repeat-x", "repeat-y", "no-repeat"}, + "boxSizing": {"content-box", "border-box"}, + "fontStyle": {"normal", "italic"}, + "fontVariantNumeric": {"normal", "tabular-nums"}, + "textAlign": {"left", "center", "right", "start", "end"}, + "verticalAlign": {"baseline", "sub", "super", "text-top", "text-bottom", "middle", "top", "bottom"}, + "textDecoration": {"none", "underline", "line-through"}, + "textTransform": {"none", "uppercase", "lowercase", "capitalize"}, + "textOverflow": {"clip", "ellipsis"}, + "whiteSpace": {"normal", "nowrap", "pre", "pre-wrap", "pre-line"}, + "wordBreak": {"normal", "break-all", "keep-all", "break-word"}, + "overflowWrap": {"normal", "break-word", "anywhere"}, + "display": {"block", "inline", "inline-block", "flex", "inline-flex", "grid"}, + "flexDirection": {"row", "row-reverse", "column", "column-reverse"}, + "flexWrap": {"nowrap", "wrap", "wrap-reverse"}, + "alignItems": {"stretch", "flex-start", "center", "flex-end", "baseline"}, + "alignContent": {"stretch", "flex-start", "center", "flex-end", "space-between", "space-around", "space-evenly"}, + "alignSelf": {"auto", "stretch", "flex-start", "center", "flex-end", "baseline"}, + "justifyContent": {"flex-start", "center", "flex-end", "space-between", "space-around", "space-evenly"}, + "justifySelf": {"auto", "stretch", "start", "center", "end"}, + "justifyItems": {"auto", "normal", "stretch", "start", "center", "end"}, + "placeItems": {"normal", "stretch", "start", "center", "end"}, + "placeContent": {"normal", "stretch", "start", "center", "end", "space-between", "space-around", "space-evenly"}, + "gridAutoFlow": {"row", "column", "dense", "row dense", "column dense"}, + "position": {"static", "relative", "absolute", "fixed", "sticky"}, + "overflow": {"visible", "hidden", "clip", "auto", "scroll"}, + "overflowX": {"visible", "hidden", "clip", "auto", "scroll"}, + "overflowY": {"visible", "hidden", "clip", "auto", "scroll"}, + "cursor": {"default", "pointer", "text", "move", "grab", "grabbing", "wait", "not-allowed"}, + "objectFit": {"fill", "contain", "cover", "none", "scale-down"}, + "objectPosition": {"center", "top", "right", "bottom", "left"}, + "mixBlendMode": {"normal", "multiply", "screen", "overlay", "darken", "lighten", "soft-light", "hard-light"}, + "isolation": {"auto", "isolate"}, +} + +BACKGROUND_SIZE_VALUES = {"auto", "cover", "contain"} +BACKGROUND_POSITION_VALUES = {"center", "top", "right", "bottom", "left"} +BACKGROUND_REPEAT_VALUES = {"repeat", "repeat-x", "repeat-y", "no-repeat"} +BACKGROUND_BLEND_VALUES = {"normal", "multiply", "screen", "overlay", "darken", "lighten", "soft-light", "hard-light"} +TRANSITION_PROPERTIES = { + "all", "background-color", "background-position", "border-color", "border-radius", + "box-shadow", "color", "filter", "gap", "height", "margin", "opacity", "padding", + "width", "transform", +} +XTERM_COLOR_KEYS = { + "background", "foreground", "cursor", "cursorAccent", "selectionBackground", + "selectionForeground", "selectionInactiveBackground", "black", "red", "green", + "yellow", "blue", "magenta", "cyan", "white", "brightBlack", "brightRed", + "brightGreen", "brightYellow", "brightBlue", "brightMagenta", "brightCyan", "brightWhite", +} +MERMAID_KEYS = [ + "nodeFill", "nodeFillHover", "nodeText", "nodeStroke", "nodeStrokeHover", + "clusterFill", "clusterText", "clusterStroke", "edgeStroke", "edgeLabelBackground", + "edgeLabelText", "noteFill", "noteText", "noteStroke", "activationFill", + "activationStroke", "success", "warning", "error", "errorBackground", "info", + "highlight", "pieColors", +] +CANVAS_COLOR_KEYS = ["bg", "panel", "fg", "muted", "border", "accent", "success", "warning", "danger", "info"] +MIME_BY_EXTENSION = { + ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", + ".webp": "image/webp", ".gif": "image/gif", ".mp4": "video/mp4", ".webm": "video/webm", +} + + +class AppearanceError(Exception): + pass + + +def is_record(value: Any) -> bool: + return isinstance(value, dict) + + +def finite_number(value: Any, minimum: float, maximum: float) -> bool: + return isinstance(value, (int, float)) and not isinstance(value, bool) and minimum <= value <= maximum + + +def load_registry() -> dict[str, Any]: + try: + registry = json.loads(REGISTRY_PATH.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as error: + raise AppearanceError(f"Could not load bundled Appearance registry: {error}") from error + if registry.get("schema") != "bitfun.appearance.registry" or registry.get("schemaVersion") != 1: + raise AppearanceError("Bundled Appearance registry has an unsupported schema") + return registry + + +def known_keys(value: Mapping[str, Any], allowed: Iterable[str], path: str, error: Callable[[str, str, str], None]) -> None: + allowed_set = set(allowed) + for key in value: + if key not in allowed_set: + error(key if path == "$" else f"{path}.{key}", "UNKNOWN_FIELD", f"Unknown field: {key}") + + +class ManifestValidator: + def __init__(self, registry: Mapping[str, Any]): + self.registry = registry + self.components = {item["id"]: item for item in registry.get("components", [])} + self.scenes = {item["id"]: item for item in registry.get("scenes", [])} + self.renderers = set(registry.get("renderers", [])) + self.css_tokens = set(registry.get("cssTokenNames", [])) + self.widget_vars = set(registry.get("widgetVariableNames", [])) + self.errors: list[dict[str, str]] = [] + self.warnings: list[dict[str, str]] = [] + + def error(self, path: str, code: str, message: str) -> None: + self.errors.append({"path": path, "code": code, "message": message}) + + def warning(self, path: str, code: str, message: str) -> None: + self.warnings.append({"path": path, "code": code, "message": message}) + + def validate(self, value: Any) -> list[dict[str, str]]: + self.errors = [] + self.warnings = [] + if not is_record(value): + self.error("$", "INVALID_PACKAGE", "Appearance package must be an object") + return self.errors + known_keys(value, [ + "schema", "schemaVersion", "id", "name", "author", "description", "version", "mode", + "preview", "backgroundMedia", "requiredCapabilities", "globals", "materials", "components", "scenes", "renderers", + "assets", "integrity", + ], "$", self.error) + if value.get("schema") != SCHEMA: + self.error("schema", "INVALID_SCHEMA", f"Schema must be {SCHEMA}") + if value.get("schemaVersion") != SCHEMA_VERSION: + self.error("schemaVersion", "UNSUPPORTED_SCHEMA_VERSION", f"Schema version must be {SCHEMA_VERSION}") + self.validate_id(value.get("id"), "id") + name = value.get("name") + if not isinstance(name, str) or not name.strip() or len(name) > 100: + self.error("name", "INVALID_NAME", "Name must be between 1 and 100 characters") + for field, limit in (("author", 100), ("description", 500)): + field_value = value.get(field) + if field_value is not None and (not isinstance(field_value, str) or len(field_value) > limit): + self.error(field, f"INVALID_{field.upper()}", f"Invalid {field}") + version = value.get("version") + if not isinstance(version, str) or not VERSION_PATTERN.fullmatch(version): + self.error("version", "INVALID_VERSION", "Version must use semantic version syntax") + if value.get("mode") not in ("light", "dark"): + self.error("mode", "INVALID_MODE", "Mode must be light or dark") + capabilities = value.get("requiredCapabilities") + if capabilities is not None: + supported = {"components.v1", "scenes.v1", "renderers.v1", "assets.v1", "background-media.v1"} + if not isinstance(capabilities, list): + self.error("requiredCapabilities", "INVALID_CAPABILITIES", "Required capabilities must be an array") + else: + for index, capability in enumerate(capabilities): + if capability not in supported: + self.error(f"requiredCapabilities.{index}", "UNSUPPORTED_CAPABILITY", f"Unsupported capability: {capability}") + self.validate_globals(value.get("globals"), "globals") + self.validate_materials(value.get("materials"), "materials") + self.validate_surfaces(value.get("components"), "components", self.components, value.get("materials")) + self.validate_surfaces(value.get("scenes"), "scenes", self.scenes, value.get("materials")) + self.validate_renderers(value.get("renderers")) + self.validate_assets(value.get("assets")) + self.validate_preview(value.get("preview"), value.get("assets")) + self.validate_background_media(value.get("backgroundMedia"), value.get("assets")) + self.validate_video_asset_usage( + value.get("backgroundMedia"), + value.get("assets"), + value.get("requiredCapabilities"), + ) + self.validate_asset_references(value) + self.validate_integrity(value.get("integrity"), value.get("assets")) + self.audit_cascade_usage(value.get("components"), value.get("scenes")) + self.validate_reference_graph(value) + return self.errors + + def validate_id(self, value: Any, path: str) -> None: + if not isinstance(value, str) or len(value) > 100 or not ID_PATTERN.fullmatch(value): + self.error(path, "INVALID_ID", "Id must be a lowercase dotted or dashed identifier") + + def validate_globals(self, value: Any, path: str) -> None: + if value is None: + return + if not is_record(value): + self.error(path, "INVALID_GLOBALS", "Globals must be an object") + return + validators = { + "colors": self.validate_color, + "lengths": self.validate_length, + "numbers": self.validate_number, + "durations": self.validate_duration, + "easings": self.validate_easing, + "fontFamilies": self.validate_font_family, + "shadows": self.validate_shadow, + } + for group, entries in value.items(): + validator = validators.get(group) + if validator is None: + self.error(f"{path}.{group}", "UNKNOWN_GLOBAL_GROUP", f"Unknown global token group: {group}") + continue + if not is_record(entries): + self.error(f"{path}.{group}", "INVALID_TOKEN_GROUP", "Token group must be an object") + continue + for token_id, token in entries.items(): + self.validate_id(token_id, f"{path}.{group}.{token_id}") + validator(token, f"{path}.{group}.{token_id}") + + def validate_materials(self, value: Any, path: str) -> None: + if value is None: + return + if not is_record(value): + self.error(path, "INVALID_MATERIALS", "Materials must be an object") + return + for material_id, definition in value.items(): + self.validate_id(material_id, f"{path}.{material_id}") + material_path = f"{path}.{material_id}" + if not is_record(definition) or not is_record(definition.get("style")): + self.error(material_path, "INVALID_MATERIAL", "Material must contain a style object") + continue + known_keys(definition, ["style", "visualRole"], material_path, self.error) + visual_role = definition.get("visualRole") + if visual_role is not None and visual_role not in VISUAL_ROLES: + self.error(f"{material_path}.visualRole", "INVALID_VISUAL_ROLE", f"Unknown visual role {visual_role}") + self.validate_style(definition["style"], f"{material_path}.style", None) + + def validate_surfaces(self, value: Any, path: str, descriptors: Mapping[str, Any], materials: Any) -> None: + if value is None: + return + if not is_record(value): + self.error(path, "INVALID_SURFACES", "Surface definitions must be an object") + return + for surface_id, surface in value.items(): + descriptor = descriptors.get(surface_id) + if descriptor is None: + self.error(f"{path}.{surface_id}", "UNKNOWN_SURFACE", f"No registered appearance contract for {surface_id}") + continue + self.validate_surface(surface, f"{path}.{surface_id}", descriptor, materials) + + def validate_surface(self, value: Any, path: str, descriptor: Mapping[str, Any], materials: Any) -> None: + if not is_record(value) or not is_record(value.get("parts")): + self.error(path, "INVALID_SURFACE", "Surface must contain a parts object") + return + known_keys(value, ["parts"], path, self.error) + parts = {part["id"]: part for part in descriptor.get("parts", [])} + facets = {facet["id"]: facet for facet in descriptor.get("facets", [])} + states = {state["id"] for state in descriptor.get("states", [])} + for part_id, rule in value["parts"].items(): + part_path = f"{path}.parts.{part_id}" + part = parts.get(part_id) + if part is None: + self.error(part_path, "UNKNOWN_PART", f"Unknown part {part_id}") + continue + if not is_record(rule): + self.error(part_path, "INVALID_PART_RULE", "Part rule must be an object") + continue + known_keys( + rule, + ["cascade", "materials", "decorationIntent", "base", "facets", "states", "contexts"], + part_path, + self.error, + ) + if rule.get("cascade") is not None and rule.get("cascade") not in ("normal", "override"): + self.error(f"{part_path}.cascade", "INVALID_CASCADE", "Cascade must be normal or override") + decoration_intent = rule.get("decorationIntent") + if decoration_intent is not None and decoration_intent not in ("flat", "separator", "framed"): + self.error( + f"{part_path}.decorationIntent", + "INVALID_DECORATION_INTENT", + "Decoration intent must be flat, separator, or framed", + ) + allowed = part.get("allowedProperties") or PROPERTY_PROFILES.get(part.get("propertyProfile", "container")) + material_ids = rule.get("materials") + material_styles: list[Mapping[str, Any]] = [] + if material_ids is not None: + if not isinstance(material_ids, list) or not 1 <= len(material_ids) <= 8: + self.error( + f"{part_path}.materials", + "INVALID_MATERIAL_LIST", + "Materials must contain between 1 and 8 material ids", + ) + else: + for index, material_id in enumerate(material_ids): + material_path = f"{part_path}.materials.{index}" + if not isinstance(material_id, str) or not is_record(materials) or material_id not in materials: + self.error(material_path, "UNKNOWN_MATERIAL", f"Unknown material {material_id}") + continue + definition = materials[material_id] + if is_record(definition) and is_record(definition.get("style")): + material_styles.append(definition["style"]) + self.validate_style(definition["style"], material_path, allowed) + self.validate_style(rule.get("base"), f"{part_path}.base", allowed) + effective_base: dict[str, Any] = {} + for material_style in material_styles: + effective_base.update(material_style) + if is_record(rule.get("base")): + effective_base.update(rule["base"]) + self.audit_part_visual_semantics(effective_base, rule, part, part_path) + if rule.get("facets") is not None: + if not is_record(rule["facets"]): + self.error(f"{part_path}.facets", "INVALID_FACETS", "Facets must be an object") + else: + for facet_id, options in rule["facets"].items(): + facet = facets.get(facet_id) + if facet is None: + self.error(f"{part_path}.facets.{facet_id}", "UNKNOWN_FACET", f"Unknown facet {facet_id}") + continue + if not is_record(options): + self.error(f"{part_path}.facets.{facet_id}", "INVALID_FACET_OPTIONS", "Facet options must be an object") + continue + for option, style in options.items(): + if option not in facet.get("values", []): + self.error(f"{part_path}.facets.{facet_id}.{option}", "UNKNOWN_FACET_VALUE", f"Unknown {facet_id} value {option}") + continue + style_path = f"{part_path}.facets.{facet_id}.{option}" + self.validate_style(style, style_path, allowed) + if is_record(style): + self.audit_part_visual_semantics(style, rule, part, style_path) + if rule.get("states") is not None: + if not is_record(rule["states"]): + self.error(f"{part_path}.states", "INVALID_STATES", "States must be an object") + else: + for state_id, style in rule["states"].items(): + if state_id not in states: + self.error(f"{part_path}.states.{state_id}", "UNKNOWN_STATE", f"Unknown state {state_id}") + continue + style_path = f"{part_path}.states.{state_id}" + self.validate_style(style, style_path, allowed) + if is_record(style): + self.audit_part_visual_semantics(style, rule, part, style_path) + if rule.get("contexts") is not None: + if not isinstance(rule["contexts"], list): + self.error(f"{part_path}.contexts", "INVALID_CONTEXTS", "Contexts must be an array") + else: + for index, context in enumerate(rule["contexts"]): + context_path = f"{part_path}.contexts.{index}" + if not is_record(context) or not is_record(context.get("when")): + self.error(context_path, "INVALID_CONTEXT", "Context must contain a when object") + continue + known_keys(context, ["when", "style"], context_path, self.error) + known_keys(context["when"], ["facets", "states"], f"{context_path}.when", self.error) + self.validate_condition(context["when"], context_path, facets, states) + style_path = f"{context_path}.style" + self.validate_style(context.get("style"), style_path, allowed) + if is_record(context.get("style")): + self.audit_part_visual_semantics(context["style"], rule, part, style_path) + + def validate_condition(self, value: Mapping[str, Any], path: str, facets: Mapping[str, Any], states: set[str]) -> None: + if value.get("facets") is not None: + if not is_record(value["facets"]): + self.error(f"{path}.when.facets", "INVALID_CONDITION_FACETS", "Condition facets must be an object") + else: + for facet_id, option in value["facets"].items(): + facet = facets.get(facet_id) + if facet is None or not isinstance(option, str) or option not in facet.get("values", []): + self.error(f"{path}.when.facets.{facet_id}", "INVALID_CONDITION_FACET", f"Invalid facet condition {facet_id}={option}") + if value.get("states") is not None: + if not isinstance(value["states"], list): + self.error(f"{path}.when.states", "INVALID_CONDITION_STATES", "Condition states must be an array") + else: + for index, state in enumerate(value["states"]): + if not isinstance(state, str) or state not in states: + self.error(f"{path}.when.states.{index}", "INVALID_CONDITION_STATE", f"Invalid state condition {state}") + + def validate_style(self, value: Any, path: str, allowed_properties: Sequence[str] | None) -> None: + if value is None: + return + if not is_record(value): + self.error(path, "INVALID_STYLE", "Style must be an object") + return + allowed = set(allowed_properties) if allowed_properties is not None else STYLE_PROPERTIES + self.validate_background_layers(value, path) + if value.get("borderStyle") is not None and value.get("borderWidth") is None: + self.warning( + path, + "BORDER_WIDTH_NORMALIZED", + "Border style without a global width is normalized to zero before side widths are applied", + ) + if (value.get("outlineWidth") is not None or value.get("outlineColor") is not None) and value.get("outlineStyle") is None: + self.warning( + path, + "OUTLINE_STYLE_NORMALIZED", + "Outline width/color without a style is normalized to solid", + ) + for prop, prop_value in value.items(): + prop_path = f"{path}.{prop}" + if prop not in STYLE_PROPERTIES or prop not in allowed: + self.error(prop_path, "UNSUPPORTED_STYLE_PROPERTY", f"Style property {prop} is not allowed") + continue + if prop in COLOR_PROPERTIES: + self.validate_color(prop_value, prop_path) + elif prop in LENGTH_PROPERTIES: + self.validate_length(prop_value, prop_path) + elif prop in NUMBER_PROPERTIES: + self.validate_number(prop_value, prop_path) + elif prop == "backgroundImage": + self.validate_asset_reference(prop_value, prop_path) + elif prop == "backgroundImages": + if not isinstance(prop_value, list) or not 1 <= len(prop_value) <= 8: + self.error(prop_path, "INVALID_BACKGROUND_IMAGES", "Background images must contain between 1 and 8 package asset references") + else: + for index, asset in enumerate(prop_value): + self.validate_asset_reference(asset, f"{prop_path}.{index}") + elif prop == "backgroundSizes": + if not isinstance(prop_value, list): + self.error(prop_path, "INVALID_BACKGROUND_SIZES", "Background sizes must be an array") + else: + for index, item in enumerate(prop_value): + self.validate_background_size(item, f"{prop_path}.{index}") + elif prop == "backgroundPositions": + if not isinstance(prop_value, list): + self.error(prop_path, "INVALID_BACKGROUND_POSITIONS", "Background positions must be an array") + else: + for index, item in enumerate(prop_value): + self.validate_background_position(item, f"{prop_path}.{index}") + elif prop == "backgroundRepeats": + self.validate_string_array(prop_value, prop_path, BACKGROUND_REPEAT_VALUES, "INVALID_BACKGROUND_REPEATS") + elif prop == "backgroundBlendModes": + self.validate_string_array(prop_value, prop_path, BACKGROUND_BLEND_VALUES, "INVALID_BACKGROUND_BLEND_MODES") + elif prop == "boxShadow": + if not isinstance(prop_value, list): + self.error(prop_path, "INVALID_SHADOW_LIST", "Box shadow must be an array") + else: + for index, shadow in enumerate(prop_value): + self.validate_shadow(shadow, f"{prop_path}.{index}") + elif prop == "fontFamily": + self.validate_font_family(prop_value, prop_path) + elif prop == "transition": + self.validate_transition(prop_value, prop_path) + elif prop == "transform": + self.validate_transform(prop_value, prop_path) + elif prop in ("gridTemplateColumns", "gridTemplateRows"): + self.validate_grid_template(prop_value, prop_path) + elif prop == "aspectRatio": + self.validate_ratio(prop_value, prop_path) + elif prop == "filter": + self.validate_filter(prop_value, prop_path) + elif str(prop_value) not in ENUM_VALUES.get(prop, set()): + self.error(prop_path, "INVALID_ENUM_VALUE", f"Invalid value for {prop}") + + def validate_background_layers(self, style: Mapping[str, Any], path: str) -> None: + layered = ["backgroundSizes", "backgroundPositions", "backgroundRepeats", "backgroundBlendModes"] + singular = ["backgroundImage", "backgroundSize", "backgroundPosition", "backgroundRepeat"] + images = style.get("backgroundImages") + if images is None: + for key in layered: + if style.get(key) is not None: + self.error(f"{path}.{key}", "BACKGROUND_LAYERS_REQUIRE_IMAGES", f"{key} requires backgroundImages") + return + for key in singular: + if style.get(key) is not None: + self.error(f"{path}.{key}", "CONFLICTING_BACKGROUND_FIELDS", f"{key} cannot be combined with backgroundImages") + if isinstance(images, list): + for key in layered: + values = style.get(key) + if isinstance(values, list) and len(values) != len(images): + self.error(f"{path}.{key}", "BACKGROUND_LAYER_COUNT_MISMATCH", f"{key} must contain exactly {len(images)} entries") + + def validate_asset_reference(self, value: Any, path: str) -> None: + if not is_record(value) or value.get("kind") != "asset" or not isinstance(value.get("assetId"), str) or not ID_PATTERN.fullmatch(value["assetId"]): + self.error(path, "INVALID_ASSET_REFERENCE", "Background image must be a package asset reference") + + def validate_background_size(self, value: Any, path: str) -> None: + if isinstance(value, str) and value in BACKGROUND_SIZE_VALUES: + return + if not is_record(value) or value.get("kind") != "backgroundSize": + self.error(path, "INVALID_BACKGROUND_SIZE", "Background size must be auto, cover, contain, or a structured size") + return + known_keys(value, ["kind", "width", "height"], path, self.error) + self.validate_background_dimension(value.get("width"), f"{path}.width") + if value.get("height") is not None: + self.validate_background_dimension(value["height"], f"{path}.height") + + def validate_background_position(self, value: Any, path: str) -> None: + if isinstance(value, str) and value in BACKGROUND_POSITION_VALUES: + return + if not is_record(value) or value.get("kind") != "backgroundPosition": + self.error(path, "INVALID_BACKGROUND_POSITION", "Background position must be a keyword or structured x/y position") + return + known_keys(value, ["kind", "x", "y"], path, self.error) + self.validate_background_position_coordinate(value.get("x"), f"{path}.x") + self.validate_background_position_coordinate(value.get("y"), f"{path}.y") + + def validate_background_dimension(self, value: Any, path: str) -> None: + if self.validate_reference(value, "lengths", path) or (is_record(value) and value.get("kind") == "zero"): + return + if is_record(value) and value.get("kind") in ("px", "rem", "em", "ch", "vw", "vh", "percent") and finite_number(value.get("value"), 0, 10000): + return + if is_record(value) and value.get("kind") == "lengthKeyword" and value.get("value") == "auto": + return + self.error(path, "INVALID_BACKGROUND_DIMENSION", "Background dimensions must be non-negative lengths, auto, or references") + + def validate_background_position_coordinate(self, value: Any, path: str) -> None: + if self.validate_reference(value, "lengths", path) or (is_record(value) and value.get("kind") == "zero"): + return + if is_record(value) and value.get("kind") in ("px", "rem", "em", "ch", "vw", "vh", "percent") and finite_number(value.get("value"), -10000, 10000): + return + self.error(path, "INVALID_BACKGROUND_POSITION_COORDINATE", "Background position coordinates must be lengths or references") + + def validate_string_array(self, value: Any, path: str, allowed: set[str], code: str) -> None: + if not isinstance(value, list): + self.error(path, code, "Value must be an array") + return + for index, item in enumerate(value): + if not isinstance(item, str) or item not in allowed: + self.error(f"{path}.{index}", code, f"Unsupported value {item}") + + def validate_reference(self, value: Any, expected_group: str, path: str) -> bool: + if not is_record(value) or value.get("kind") != "ref" or not isinstance(value.get("path"), str): + return False + if not REFERENCE_PATTERN.fullmatch(value["path"]): + return False + if not value["path"].startswith(f"globals.{expected_group}."): + self.error(path, "REFERENCE_TYPE_MISMATCH", f"Reference must target globals.{expected_group}") + return True + + def validate_color(self, value: Any, path: str) -> None: + if self.validate_reference(value, "colors", path): + return + if not is_record(value): + self.error(path, "INVALID_COLOR", "Color must be a structured color value") + return + if value.get("kind") == "transparent": + return + if value.get("kind") == "hex" and isinstance(value.get("value"), str) and re.fullmatch(r"#[0-9a-f]{3,8}", value["value"], re.I) and len(value["value"]) in (4, 5, 7, 9): + return + if value.get("kind") == "rgb" and all(finite_number(value.get(key), 0, 255) for key in ("r", "g", "b")) and (value.get("a") is None or finite_number(value.get("a"), 0, 1)): + return + if value.get("kind") == "hsl" and finite_number(value.get("h"), 0, 360) and finite_number(value.get("s"), 0, 100) and finite_number(value.get("l"), 0, 100) and (value.get("a") is None or finite_number(value.get("a"), 0, 1)): + return + self.error(path, "INVALID_COLOR", "Invalid structured color value") + + def validate_length(self, value: Any, path: str) -> None: + if self.validate_reference(value, "lengths", path) or (is_record(value) and value.get("kind") == "zero"): + return + if is_record(value) and value.get("kind") in ("px", "rem", "em", "ch", "vw", "vh", "percent") and finite_number(value.get("value"), -10000, 10000): + return + if is_record(value) and value.get("kind") == "lengthKeyword" and value.get("value") in ("auto", "min-content", "max-content", "fit-content"): + return + self.error(path, "INVALID_LENGTH", "Length must be a structured unit, keyword, zero, or reference value") + + def validate_number(self, value: Any, path: str) -> None: + if self.validate_reference(value, "numbers", path): + return + if is_record(value) and value.get("kind") == "number" and finite_number(value.get("value"), -10000, 10000): + return + self.error(path, "INVALID_NUMBER", "Number must be a structured number or reference value") + + def validate_duration(self, value: Any, path: str) -> None: + if self.validate_reference(value, "durations", path): + return + if is_record(value) and value.get("kind") == "ms" and finite_number(value.get("value"), 0, 10000): + return + self.error(path, "INVALID_DURATION", "Duration must be a structured millisecond or reference value") + + def validate_easing(self, value: Any, path: str) -> None: + if self.validate_reference(value, "easings", path): + return + if is_record(value) and value.get("kind") == "easing" and value.get("value") in ("linear", "standard", "decelerate", "accelerate"): + return + self.error(path, "INVALID_EASING", "Easing must be a supported structured easing value") + + def validate_font_family(self, value: Any, path: str) -> None: + if self.validate_reference(value, "fontFamilies", path): + return + if not is_record(value) or value.get("kind") != "fontFamily" or not isinstance(value.get("families"), list) or not 1 <= len(value["families"]) <= 8: + self.error(path, "INVALID_FONT_FAMILY", "Font family must contain between 1 and 8 local family names") + return + for index, family in enumerate(value["families"]): + if not isinstance(family, str) or not re.fullmatch(r"[\w .-]{1,80}", family) or FORBIDDEN_RENDERER_TEXT.search(family): + self.error(f"{path}.families.{index}", "INVALID_FONT_FAMILY_NAME", "Invalid local font family name") + + def validate_shadow(self, value: Any, path: str) -> None: + if self.validate_reference(value, "shadows", path) or (is_record(value) and value.get("kind") == "none"): + return + if not is_record(value) or value.get("kind") != "shadow": + self.error(path, "INVALID_SHADOW", "Shadow must be a structured shadow, none, or reference value") + return + self.validate_length(value.get("x"), f"{path}.x") + self.validate_length(value.get("y"), f"{path}.y") + self.validate_length(value.get("blur"), f"{path}.blur") + if value.get("spread") is not None: + self.validate_length(value["spread"], f"{path}.spread") + self.validate_color(value.get("color"), f"{path}.color") + if value.get("inset") is not None and not isinstance(value["inset"], bool): + self.error(f"{path}.inset", "INVALID_SHADOW_INSET", "Shadow inset must be boolean") + + def validate_transition(self, value: Any, path: str) -> None: + if not isinstance(value, list) or len(value) > 12: + self.error(path, "INVALID_TRANSITION", "Transition must be an array with at most 12 items") + return + for index, item in enumerate(value): + item_path = f"{path}.{index}" + if not is_record(item) or item.get("property") not in TRANSITION_PROPERTIES: + self.error(item_path, "INVALID_TRANSITION_ITEM", "Transition item has an unsupported property") + continue + self.validate_duration(item.get("duration"), f"{item_path}.duration") + self.validate_easing(item.get("easing"), f"{item_path}.easing") + if item.get("delay") is not None: + self.validate_duration(item["delay"], f"{item_path}.delay") + + def validate_transform(self, value: Any, path: str) -> None: + if not is_record(value) or value.get("kind") != "transform": + self.error(path, "INVALID_TRANSFORM", "Transform must be a structured transform value") + return + if value.get("scale") is not None and not finite_number(value["scale"], 0, 10): + self.error(f"{path}.scale", "INVALID_SCALE", "Scale must be between 0 and 10") + if value.get("translateX") is not None: + self.validate_length(value["translateX"], f"{path}.translateX") + if value.get("translateY") is not None: + self.validate_length(value["translateY"], f"{path}.translateY") + if value.get("rotate") is not None and not finite_number(value["rotate"], -3600, 3600): + self.error(f"{path}.rotate", "INVALID_ROTATION", "Rotation must be between -3600 and 3600 degrees") + + def validate_grid_template(self, value: Any, path: str) -> None: + if not is_record(value) or value.get("kind") != "gridTracks" or not isinstance(value.get("tracks"), list) or not 1 <= len(value["tracks"]) <= 24: + self.error(path, "INVALID_GRID_TEMPLATE", "Grid template must contain between 1 and 24 structured tracks") + return + for index, track in enumerate(value["tracks"]): + if is_record(track) and track.get("kind") == "fr" and finite_number(track.get("value"), 0.01, 100): + continue + if is_record(track) and track.get("kind") == "trackKeyword" and track.get("value") in ("auto", "min-content", "max-content"): + continue + self.validate_length(track, f"{path}.tracks.{index}") + + def validate_ratio(self, value: Any, path: str) -> None: + if not is_record(value) or value.get("kind") != "ratio" or not finite_number(value.get("width"), 0.01, 10000) or not finite_number(value.get("height"), 0.01, 10000): + self.error(path, "INVALID_ASPECT_RATIO", "Aspect ratio must contain positive width and height values") + + def validate_filter(self, value: Any, path: str) -> None: + if not isinstance(value, list) or len(value) > 8: + self.error(path, "INVALID_FILTER", "Filter must be an array with at most 8 items") + return + for index, item in enumerate(value): + item_path = f"{path}.{index}" + if not is_record(item): + self.error(item_path, "INVALID_FILTER_ITEM", "Filter item must be structured") + elif item.get("kind") == "blur": + self.validate_length(item.get("value"), f"{item_path}.value") + elif item.get("kind") in ("brightness", "contrast", "grayscale", "saturate"): + self.validate_number(item.get("value"), f"{item_path}.value") + elif item.get("kind") == "hueRotate" and finite_number(item.get("degrees"), -3600, 3600): + continue + else: + self.error(item_path, "INVALID_FILTER_ITEM", f"Unsupported filter {item.get('kind')}") + + def validate_renderers(self, value: Any) -> None: + if value is None: + return + if not is_record(value): + self.error("renderers", "INVALID_RENDERERS", "Renderers must be an object") + return + for renderer_id, definition in value.items(): + path = f"renderers.{renderer_id}" + if renderer_id not in self.renderers: + self.error(path, "UNKNOWN_RENDERER", f"No registered renderer adapter for {renderer_id}") + continue + if not is_record(definition) or definition.get("version") != 1 or not is_record(definition.get("settings")): + self.error(path, "INVALID_RENDERER_DEFINITION", "Renderer definition must use version 1 and contain settings") + continue + known_keys(definition, ["version", "settings"], path, self.error) + self.scan_renderer_text(definition["settings"], f"{path}.settings") + renderer_errors = self.validate_renderer_settings(renderer_id, definition["settings"]) + for message in renderer_errors: + self.error(f"{path}.settings", "INVALID_RENDERER_SETTINGS", message) + + def scan_renderer_text(self, value: Any, path: str) -> None: + if isinstance(value, str): + if FORBIDDEN_RENDERER_TEXT.search(value): + self.error(path, "FORBIDDEN_RENDERER_TEXT", "Renderer settings cannot contain URLs, markup, or CSS syntax") + elif isinstance(value, list): + for index, item in enumerate(value): + self.scan_renderer_text(item, f"{path}.{index}") + elif is_record(value): + for key, item in value.items(): + self.scan_renderer_text(item, f"{path}.{key}") + + def validate_renderer_settings(self, renderer_id: str, settings: Mapping[str, Any]) -> list[str]: + if renderer_id == "css-tokens": + return self.validate_css_tokens(settings) + if renderer_id == "monaco": + return self.validate_monaco(settings) + if renderer_id == "xterm": + return self.validate_xterm(settings) + if renderer_id == "mermaid": + return self.validate_mermaid(settings) + if renderer_id == "generative-widget": + return self.validate_widget(settings) + if renderer_id == "bitfun-canvas": + return self.validate_canvas(settings) + return [f"Unsupported renderer: {renderer_id}"] + + def validate_css_tokens(self, settings: Mapping[str, Any]) -> list[str]: + errors: list[str] = [] + tokens = settings.get("tokens") + background = settings.get("background") + if not is_record(tokens) or not isinstance(background, str): + return ["css-tokens settings must contain tokens and background"] + for name, value in tokens.items(): + if name not in self.css_tokens: + errors.append(f"Unsupported CSS token name: {name}") + if not isinstance(value, str) or not value or len(value) > 512: + errors.append(f"CSS token {name} must be a non-empty string of at most 512 characters") + elif CSS_TOKEN_FORBIDDEN.search(value): + errors.append(f"CSS token {name} contains a forbidden value") + if CSS_TOKEN_FORBIDDEN.search(background): + errors.append("css-tokens background contains a forbidden value") + return errors + + def validate_monaco(self, settings: Mapping[str, Any]) -> list[str]: + errors: list[str] = [] + known = {"id", "base", "inherit", "rules", "colors"} + for key in settings: + if key not in known: + errors.append(f"Unknown setting: {key}") + if not isinstance(settings.get("id"), str) or not re.fullmatch(r"[a-z][a-z0-9-]{0,95}", settings["id"]): + errors.append("id must contain only lowercase letters, digits, and hyphens") + if settings.get("base") not in ("vs", "vs-dark", "hc-black", "hc-light"): + errors.append("base must be a supported Monaco base theme") + if not isinstance(settings.get("inherit"), bool): + errors.append("inherit must be boolean") + rules = settings.get("rules") + if not isinstance(rules, list) or len(rules) > 512: + errors.append("rules must be an array with at most 512 entries") + else: + for index, rule in enumerate(rules): + if not is_record(rule) or not isinstance(rule.get("token"), str) or len(rule["token"]) > 160: + errors.append(f"rules.{index}.token is invalid") + continue + for key in rule: + if key not in ("token", "foreground", "background", "fontStyle"): + errors.append(f"rules.{index} has unknown field {key}") + for key in ("foreground", "background"): + color = rule.get(key) + if color is not None and (not isinstance(color, str) or not MONACO_TOKEN_COLOR_PATTERN.fullmatch(color)): + errors.append(f"rules.{index}.{key} is not a supported color") + font_style = rule.get("fontStyle") + if font_style is not None and (not isinstance(font_style, str) or not FONT_STYLE_PATTERN.fullmatch(font_style)): + errors.append(f"rules.{index}.fontStyle is invalid") + colors = settings.get("colors") + if not is_record(colors) or len(colors) > 512: + errors.append("colors must be an object with at most 512 entries") + else: + for key, color in colors.items(): + if not re.fullmatch(r"[A-Za-z][A-Za-z0-9.]{0,159}", key): + errors.append(f"colors.{key} has an invalid key") + if not isinstance(color, str) or not MONACO_COLOR_PATTERN.fullmatch(color): + errors.append(f"colors.{key} is not a supported color") + return errors + + def validate_xterm_colors(self, value: Any, path: str) -> list[str]: + if not is_record(value): + return [f"{path} must be an object"] + errors: list[str] = [] + for key, color in value.items(): + if key not in XTERM_COLOR_KEYS: + errors.append(f"{path}.{key} is not supported") + if not isinstance(color, str) or not COLOR_STRING_PATTERN.fullmatch(color): + errors.append(f"{path}.{key} is not a supported color") + for key in ("background", "foreground", "cursor"): + if not isinstance(value.get(key), str): + errors.append(f"{path}.{key} is required") + return errors + + def validate_xterm(self, settings: Mapping[str, Any]) -> list[str]: + errors: list[str] = [] + for key in settings: + if key not in ("surfaces", "fontWeight", "fontWeightBold"): + errors.append(f"Unknown setting: {key}") + surfaces = settings.get("surfaces") + if not is_record(surfaces): + errors.append("surfaces must be an object") + else: + for key in surfaces: + if key not in ("terminal", "output"): + errors.append(f"Unknown xterm surface: {key}") + errors.extend(self.validate_xterm_colors(surfaces.get("terminal"), "surfaces.terminal")) + errors.extend(self.validate_xterm_colors(surfaces.get("output"), "surfaces.output")) + if settings.get("fontWeight") not in ("normal", "500"): + errors.append("fontWeight is invalid") + if settings.get("fontWeightBold") not in ("bold", "700"): + errors.append("fontWeightBold is invalid") + return errors + + def validate_mermaid(self, settings: Mapping[str, Any]) -> list[str]: + errors: list[str] = [] + for key in settings: + if key not in ("mode", "palette"): + errors.append(f"Unknown setting: {key}") + if settings.get("mode") not in ("light", "dark"): + errors.append("mode must be light or dark") + palette = settings.get("palette") + if not is_record(palette): + errors.append("palette must be an object") + return errors + for key in palette: + if key not in MERMAID_KEYS: + errors.append(f"palette.{key} is not supported") + for key in MERMAID_KEYS: + color = palette.get(key) + if key == "pieColors": + if not isinstance(color, list) or len(color) != 8 or any(not isinstance(item, str) or not COLOR_STRING_PATTERN.fullmatch(item) for item in color): + errors.append("palette.pieColors must contain exactly eight supported colors") + elif not isinstance(color, str) or not COLOR_STRING_PATTERN.fullmatch(color): + errors.append(f"palette.{key} is not a supported color") + return errors + + def validate_widget(self, settings: Mapping[str, Any]) -> list[str]: + errors: list[str] = [] + for key in settings: + if key not in ("id", "mode", "vars"): + errors.append(f"Unknown setting: {key}") + if not isinstance(settings.get("id"), str) or not re.fullmatch(r"[a-z][a-z0-9.-]{0,95}", settings["id"]): + errors.append("id is invalid") + if settings.get("mode") not in ("light", "dark"): + errors.append("mode must be light or dark") + variables = settings.get("vars") + if not is_record(variables) or len(variables) > 256: + errors.append("vars must be an object with at most 256 entries") + else: + for key, value in variables.items(): + if key not in self.widget_vars: + errors.append(f"vars.{key} is not registered by the widget host contract") + if not isinstance(value, str) or len(value) > 256 or not WIDGET_SAFE_VALUE.fullmatch(value): + errors.append(f"vars.{key} has an unsafe value") + return errors + + def validate_canvas(self, settings: Mapping[str, Any]) -> list[str]: + errors: list[str] = [] + known = {"id", "mode", *CANVAS_COLOR_KEYS} + for key in settings: + if key not in known: + errors.append(f"Unknown setting: {key}") + if not isinstance(settings.get("id"), str) or not re.fullmatch(r"[a-z][a-z0-9.-]{0,95}", settings["id"]): + errors.append("id is invalid") + if settings.get("mode") not in ("light", "dark"): + errors.append("mode must be light or dark") + for key in CANVAS_COLOR_KEYS: + color = settings.get(key) + if not isinstance(color, str) or not COLOR_STRING_PATTERN.fullmatch(color): + errors.append(f"{key} is not a supported color") + return errors + + def validate_assets(self, value: Any) -> None: + if value is None: + return + if not is_record(value): + self.error("assets", "INVALID_ASSETS", "Assets must be an object") + return + for asset_id, asset in value.items(): + path = f"assets.{asset_id}" + self.validate_id(asset_id, path) + valid_image = is_record(asset) and asset.get("kind") == "image" and asset.get("mimeType") in ("image/png", "image/jpeg", "image/webp", "image/gif") + valid_video = is_record(asset) and asset.get("kind") == "video" and asset.get("mimeType") in ("video/mp4", "video/webm") + if not valid_image and not valid_video: + self.error(path, "INVALID_ASSET", "Asset must be a supported image or video") + continue + known_keys(asset, ["kind", "mimeType", "source"], path, self.error) + source = asset.get("source") + if not is_record(source) or source.get("kind") != "package" or not isinstance(source.get("path"), str) or not safe_package_path(source["path"]): + self.error(f"{path}.source", "INVALID_ASSET_PATH", "Asset path must be a safe package-relative path") + else: + known_keys(source, ["kind", "path"], f"{path}.source", self.error) + + def validate_preview(self, value: Any, assets: Any) -> None: + if value is None: + return + if not is_record(value) or value.get("kind") != "asset" or not isinstance(value.get("assetId"), str): + self.error("preview", "INVALID_PREVIEW", "Preview must reference a declared image asset") + return + if not is_record(assets) or value["assetId"] not in assets: + self.error("preview.assetId", "UNKNOWN_ASSET_REFERENCE", f"Unknown appearance asset {value['assetId']}") + elif not is_record(assets[value["assetId"]]) or assets[value["assetId"]].get("kind") != "image": + self.error("preview.assetId", "PREVIEW_ASSET_NOT_IMAGE", "Preview must reference an image asset") + + def validate_background_media(self, value: Any, assets: Any) -> None: + if value is None: + return + if not is_record(value) or value.get("kind") != "video": + self.error("backgroundMedia", "INVALID_BACKGROUND_MEDIA", "Background media must be a video declaration") + return + known_keys(value, ["kind", "assetId", "posterAssetId", "fit", "position"], "backgroundMedia", self.error) + for field, expected_kind in (("assetId", "video"), ("posterAssetId", "image")): + asset_id = value.get(field) + if not isinstance(asset_id, str): + self.error(f"backgroundMedia.{field}", "INVALID_BACKGROUND_MEDIA_ASSET", f"{field} must reference a declared {expected_kind} asset") + elif not is_record(assets) or asset_id not in assets: + self.error(f"backgroundMedia.{field}", "UNKNOWN_ASSET_REFERENCE", f"Unknown appearance asset {asset_id}") + elif not is_record(assets[asset_id]) or assets[asset_id].get("kind") != expected_kind: + self.error(f"backgroundMedia.{field}", "BACKGROUND_MEDIA_ASSET_KIND_MISMATCH", f"{field} must reference a {expected_kind} asset") + if value.get("fit") is not None and value.get("fit") not in ("cover", "contain"): + self.error("backgroundMedia.fit", "INVALID_BACKGROUND_MEDIA_FIT", "Background media fit must be cover or contain") + if value.get("position") is not None and value.get("position") not in BACKGROUND_POSITION_VALUES: + self.error("backgroundMedia.position", "INVALID_BACKGROUND_MEDIA_POSITION", "Background media position is invalid") + + def validate_video_asset_usage(self, background_media: Any, assets: Any, capabilities: Any) -> None: + if background_media is not None and ( + not isinstance(capabilities, list) or "background-media.v1" not in capabilities + ): + self.error( + "requiredCapabilities", + "MISSING_BACKGROUND_MEDIA_CAPABILITY", + "Background media requires the background-media.v1 capability", + ) + selected_video_id = background_media.get("assetId") if is_record(background_media) else None + if not is_record(assets): + return + for asset_id, asset in assets.items(): + if is_record(asset) and asset.get("kind") == "video" and asset_id != selected_video_id: + self.error( + f"assets.{asset_id}", + "UNUSED_VIDEO_ASSET", + "Video assets are allowed only when referenced by top-level backgroundMedia", + ) + + def validate_integrity(self, value: Any, assets: Any) -> None: + if value is None: + return + if not is_record(value) or not is_record(value.get("sha256")): + self.error("integrity", "INVALID_INTEGRITY", "Integrity must contain a sha256 object") + return + for asset_id, digest in value["sha256"].items(): + if not is_record(assets) or asset_id not in assets: + self.error(f"integrity.sha256.{asset_id}", "UNKNOWN_INTEGRITY_ASSET", f"Unknown integrity asset {asset_id}") + if not isinstance(digest, str) or not re.fullmatch(r"[0-9a-f]{64}", digest, re.I): + self.error(f"integrity.sha256.{asset_id}", "INVALID_SHA256", "SHA-256 digest must contain 64 hexadecimal characters") + + def validate_asset_references(self, manifest: Mapping[str, Any]) -> None: + assets = manifest.get("assets") + declared = set(assets) if is_record(assets) else set() + for role, style in iter_styles(manifest): + references: list[tuple[Any, str]] = [] + if is_record(style) and style.get("backgroundImage") is not None: + references.append((style["backgroundImage"], f"{role}.backgroundImage")) + if is_record(style) and isinstance(style.get("backgroundImages"), list): + references.extend((item, f"{role}.backgroundImages.{index}") for index, item in enumerate(style["backgroundImages"])) + for reference, path in references: + if not is_record(reference) or reference.get("kind") != "asset" or not isinstance(reference.get("assetId"), str): + continue + asset_id = reference["assetId"] + if asset_id not in declared: + self.error(path, "UNKNOWN_ASSET_REFERENCE", f"Unknown appearance asset {asset_id}") + elif is_record(assets) and is_record(assets[asset_id]) and assets[asset_id].get("kind") != "image": + self.error(path, "BACKGROUND_ASSET_NOT_IMAGE", "Style background assets must be images") + + def validate_reference_graph(self, manifest: Mapping[str, Any]) -> None: + globals_value = manifest.get("globals") + globals_map = globals_value if is_record(globals_value) else {} + token_paths: set[str] = set() + references: list[tuple[str, str]] = [] + graph: dict[str, set[str]] = {} + + for group, entries in globals_map.items(): + if not is_record(entries): + continue + for token_id in entries: + token_paths.add(f"globals.{group}.{token_id}") + + def scan(value: Any, path: str, owner_token: str | None = None) -> None: + if isinstance(value, list): + for index, item in enumerate(value): + scan(item, f"{path}.{index}", owner_token) + return + if not is_record(value): + return + if value.get("kind") == "ref" and isinstance(value.get("path"), str) and REFERENCE_PATTERN.fullmatch(value["path"]): + references.append((path, value["path"])) + if owner_token is not None: + graph.setdefault(owner_token, set()).add(value["path"]) + return + for key, child in value.items(): + child_path = key if path == "$" else f"{path}.{key}" + scan(child, child_path, owner_token) + + for group, entries in globals_map.items(): + if not is_record(entries): + continue + for token_id, value in entries.items(): + token_path = f"globals.{group}.{token_id}" + scan(value, token_path, token_path) + for key, value in manifest.items(): + if key != "globals": + scan(value, key) + + for source_path, target_path in references: + if target_path not in token_paths: + self.error( + source_path, + "UNKNOWN_TOKEN_REFERENCE", + f"Unknown appearance token reference: {target_path}", + ) + + visited: set[str] = set() + visiting: set[str] = set() + + def visit(token_path: str) -> None: + if token_path in visited: + return + if token_path in visiting: + self.error( + token_path, + "CIRCULAR_TOKEN_REFERENCE", + f"Circular appearance token reference: {token_path}", + ) + return + visiting.add(token_path) + for target_path in graph.get(token_path, set()): + if target_path in token_paths: + visit(target_path) + visiting.remove(token_path) + visited.add(token_path) + + for token_path in token_paths: + visit(token_path) + + def audit_part_visual_semantics( + self, + style: Mapping[str, Any], + rule: Mapping[str, Any], + part: Mapping[str, Any], + path: str, + ) -> None: + if ( + not part.get("continuityGroup") + and part.get("visualRole") != "continuous-surface" + ) or rule.get("decorationIntent") == "framed": + return + radius_properties = ( + "borderRadius", + "borderTopLeftRadius", + "borderTopRightRadius", + "borderBottomRightRadius", + "borderBottomLeftRadius", + ) + shadows = style.get("boxShadow") + has_full_frame = ( + self.has_non_zero_length(style.get("borderWidth")) + or any(self.has_non_zero_length(style.get(prop)) for prop in radius_properties) + or ( + isinstance(shadows, list) + and any(not is_record(shadow) or shadow.get("kind") != "none" for shadow in shadows) + ) + ) + if not has_full_frame: + return + suffix = f" {part['continuityGroup']}" if part.get("continuityGroup") else "" + self.warning( + path, + "CONTINUOUS_SURFACE_FRAMED", + f"Continuous surface{suffix} uses a full frame without decorationIntent=framed", + ) + + @staticmethod + def has_non_zero_length(value: Any) -> bool: + if not is_record(value): + return value is not None + if value.get("kind") == "zero": + return False + return not (isinstance(value.get("value"), (int, float)) and value.get("value") == 0) + + def audit_cascade_usage(self, components: Any, scenes: Any) -> None: + total = 0 + overrides = 0 + for surfaces in (components, scenes): + if not is_record(surfaces): + continue + for surface in surfaces.values(): + if not is_record(surface) or not is_record(surface.get("parts")): + continue + for rule in surface["parts"].values(): + if not is_record(rule): + continue + total += 1 + if rule.get("cascade") == "override": + overrides += 1 + if total >= 4 and overrides / total > 0.25: + self.warning( + "$", + "EXCESSIVE_OVERRIDE_USAGE", + f"{overrides} of {total} part rules use override; override should be reserved for specific paint conflicts", + ) + + +def safe_package_path(value: str) -> bool: + if not ASSET_PATH_PATTERN.fullmatch(value) or ".." in value or "\\" in value or value.startswith("/"): + return False + parts = PurePosixPath(value).parts + return bool(parts) and all(part not in ("", ".", "..") for part in parts) + + +def iter_styles(manifest: Mapping[str, Any]) -> Iterable[tuple[str, Mapping[str, Any]]]: + materials = manifest.get("materials") + if is_record(materials): + for material_id, definition in materials.items(): + if is_record(definition) and is_record(definition.get("style")): + yield f"materials.{material_id}.style", definition["style"] + for group_name in ("components", "scenes"): + surfaces = manifest.get(group_name) + if not is_record(surfaces): + continue + for surface_id, surface in surfaces.items(): + if not is_record(surface) or not is_record(surface.get("parts")): + continue + for part_id, rule in surface["parts"].items(): + if not is_record(rule): + continue + base_path = f"{group_name}.{surface_id}.parts.{part_id}" + if is_record(rule.get("base")): + yield f"{base_path}.base", rule["base"] + facets = rule.get("facets") + if is_record(facets): + for facet_id, options in facets.items(): + if is_record(options): + for option, style in options.items(): + if is_record(style): + yield f"{base_path}.facets.{facet_id}.{option}", style + states = rule.get("states") + if is_record(states): + for state_id, style in states.items(): + if is_record(style): + yield f"{base_path}.states.{state_id}", style + contexts = rule.get("contexts") + if isinstance(contexts, list): + for index, context in enumerate(contexts): + if is_record(context) and is_record(context.get("style")): + yield f"{base_path}.contexts.{index}.style", context["style"] + + +def read_json(path: Path) -> dict[str, Any]: + try: + value = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as error: + raise AppearanceError(f"Could not read {path}: {error}") from error + if not is_record(value): + raise AppearanceError(f"JSON root must be an object: {path}") + return value + + +def manifest_bytes(manifest: Mapping[str, Any]) -> bytes: + return (json.dumps(manifest, ensure_ascii=False, indent=2) + "\n").encode("utf-8") + + +def read_image_info(data: bytes) -> tuple[str, int, int]: + if len(data) >= 24 and data[0] == 0x89 and data[1:4] == b"PNG": + return "image/png", int.from_bytes(data[16:20], "big"), int.from_bytes(data[20:24], "big") + if len(data) >= 10 and data[:6] in (b"GIF87a", b"GIF89a"): + return "image/gif", int.from_bytes(data[6:8], "little"), int.from_bytes(data[8:10], "little") + if len(data) >= 30 and data[:4] == b"RIFF" and data[8:12] == b"WEBP": + chunk = data[12:16] + if chunk == b"VP8X": + return "image/webp", 1 + int.from_bytes(data[24:27], "little"), 1 + int.from_bytes(data[27:30], "little") + if chunk == b"VP8 ": + return "image/webp", int.from_bytes(data[26:28], "little") & 0x3FFF, int.from_bytes(data[28:30], "little") & 0x3FFF + if chunk == b"VP8L" and len(data) >= 25 and data[20] == 0x2F: + bits = int.from_bytes(data[21:25], "little") + return "image/webp", (bits & 0x3FFF) + 1, ((bits >> 14) & 0x3FFF) + 1 + raise AppearanceError("Appearance WebP dimensions could not be read") + if len(data) >= 4 and data[:2] == b"\xff\xd8": + offset = 2 + sof = {0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF} + while offset + 8 < len(data): + if data[offset] != 0xFF: + offset += 1 + continue + marker = data[offset + 1] + if marker in (0xD8, 0xD9): + offset += 2 + continue + length = int.from_bytes(data[offset + 2:offset + 4], "big") + if length < 2 or offset + 2 + length > len(data): + break + if marker in sof: + return "image/jpeg", int.from_bytes(data[offset + 7:offset + 9], "big"), int.from_bytes(data[offset + 5:offset + 7], "big") + offset += 2 + length + raise AppearanceError("Appearance JPEG dimensions could not be read") + raise AppearanceError("Appearance asset is not a supported image") + + +def assert_image_limits(path: str, data: bytes, expected_mime: str, max_bytes: int = MAX_IMAGE_BYTES) -> tuple[int, int]: + if not 1 <= len(data) <= max_bytes: + raise AppearanceError(f"Appearance asset has invalid size: {path}") + mime, width, height = read_image_info(data) + if mime != expected_mime: + raise AppearanceError(f"Appearance asset MIME mismatch: {path}; declared {expected_mime}, found {mime}") + if width <= 0 or height <= 0 or width > MAX_DIMENSION or height > MAX_DIMENSION or width * height > MAX_PIXELS: + raise AppearanceError(f"Appearance image dimensions exceed the allowed limit: {path}") + return width, height + + +def read_video_mime(data: bytes) -> str: + if len(data) >= 12 and data[4:8] == b"ftyp": + return "video/mp4" + if len(data) >= 16 and data[:4] == b"\x1a\x45\xdf\xa3" and b"webm" in data[:4096].lower(): + return "video/webm" + raise AppearanceError("Appearance asset is not a supported video") + + +def assert_video_limits(path: str, data: bytes, expected_mime: str) -> None: + if not 1 <= len(data) <= MAX_VIDEO_BYTES: + raise AppearanceError(f"Appearance asset has invalid size: {path}") + actual_mime = read_video_mime(data) + if actual_mime != expected_mime: + raise AppearanceError(f"Appearance asset MIME mismatch: {path}; declared {expected_mime}, found {actual_mime}") + + +def assert_asset_limits( + path: str, + data: bytes, + definition: Mapping[str, Any], + preview_asset: bool = False, +) -> None: + if definition.get("kind") == "video": + assert_video_limits(path, data, definition["mimeType"]) + return + assert_image_limits( + path, + data, + definition["mimeType"], + MAX_PREVIEW_BYTES if preview_asset else MAX_IMAGE_BYTES, + ) + + +def format_issues(issues: Sequence[Mapping[str, str]]) -> str: + return "\n".join(f"- {issue['path']} [{issue['code']}]: {issue['message']}" for issue in issues) + + +def validate_manifest(manifest: Mapping[str, Any], registry: Mapping[str, Any]) -> list[dict[str, str]]: + validator = ManifestValidator(registry) + issues = validator.validate(manifest) + if issues: + raise AppearanceError(f"Invalid appearance manifest:\n{format_issues(issues)}") + return validator.warnings + + +def declared_assets(manifest: Mapping[str, Any]) -> dict[str, Mapping[str, Any]]: + assets = manifest.get("assets") + return dict(assets) if is_record(assets) else {} + + +def validate_project( + project: Path, + registry: Mapping[str, Any], + warning_sink: list[dict[str, str]] | None = None, +) -> dict[str, Any]: + project = project.resolve() + manifest_path = project / MANIFEST_NAME + if not project.is_dir() or not manifest_path.is_file(): + raise AppearanceError(f"Project must contain {MANIFEST_NAME}: {project}") + raw_manifest = manifest_path.read_bytes() + if not 1 <= len(raw_manifest) <= MAX_MANIFEST_BYTES: + raise AppearanceError("Appearance manifest has invalid size") + manifest = read_json(manifest_path) + warnings = validate_manifest(manifest, registry) + if warning_sink is not None: + warning_sink.extend(warnings) + assets = declared_assets(manifest) + preview_asset_id = manifest.get("preview", {}).get("assetId") if is_record(manifest.get("preview")) else None + allowed = {MANIFEST_NAME} + total = len(raw_manifest) + seen_paths: set[str] = set() + for asset_id, definition in assets.items(): + package_path = definition["source"]["path"] + if package_path in seen_paths: + raise AppearanceError(f"Appearance assets cannot share package path: {package_path}") + seen_paths.add(package_path) + allowed.add(package_path) + file_path = project.joinpath(*PurePosixPath(package_path).parts).resolve() + try: + file_path.relative_to(project) + except ValueError as error: + raise AppearanceError(f"Asset escapes project directory: {package_path}") from error + if not file_path.is_file(): + raise AppearanceError(f"Declared appearance asset is missing: {package_path}") + data = file_path.read_bytes() + total += len(data) + if total > MAX_EXPANDED_BYTES: + raise AppearanceError("Appearance project exceeds the expanded size limit") + assert_asset_limits(package_path, data, definition, asset_id == preview_asset_id) + digest = manifest.get("integrity", {}).get("sha256", {}).get(asset_id) if is_record(manifest.get("integrity")) else None + if digest and hashlib.sha256(data).hexdigest() != digest.lower(): + raise AppearanceError(f"Appearance asset integrity mismatch: {package_path}") + actual = { + path.relative_to(project).as_posix() + for path in project.rglob("*") + if path.is_file() + } + undeclared = sorted(actual - allowed) + if undeclared: + raise AppearanceError(f"Undeclared project files: {', '.join(undeclared)}") + if len(actual) == 0 or len(actual) > MAX_ENTRIES: + raise AppearanceError(f"Appearance project must contain between 1 and {MAX_ENTRIES} files") + return manifest + + +def zip_is_symlink(info: zipfile.ZipInfo) -> bool: + return ((info.external_attr >> 16) & 0o170000) == 0o120000 + + +def validate_archive( + archive_path: Path, + registry: Mapping[str, Any], + warning_sink: list[dict[str, str]] | None = None, +) -> dict[str, Any]: + archive_path = archive_path.resolve() + if not archive_path.is_file() or not 1 <= archive_path.stat().st_size <= MAX_ARCHIVE_BYTES: + raise AppearanceError(f"Appearance archive must be between 1 byte and {MAX_ARCHIVE_BYTES} bytes") + try: + with zipfile.ZipFile(archive_path) as archive: + infos = [info for info in archive.infolist() if not info.is_dir()] + names = [info.filename for info in infos] + if not 1 <= len(infos) <= MAX_ENTRIES: + raise AppearanceError(f"Appearance archive must contain between 1 and {MAX_ENTRIES} files") + if len(names) != len(set(names)): + raise AppearanceError("Appearance archive contains duplicate paths") + for info in infos: + if not safe_package_path(info.filename): + raise AppearanceError(f"Unsafe archive path: {info.filename}") + if zip_is_symlink(info): + raise AppearanceError(f"Symbolic links are not allowed: {info.filename}") + if info.flag_bits & 0x1: + raise AppearanceError(f"Encrypted appearance archive entries are not supported: {info.filename}") + if info.file_size > MAX_VIDEO_BYTES: + raise AppearanceError(f"Appearance archive entry is too large: {info.filename}") + if sum(info.file_size for info in infos) > MAX_EXPANDED_BYTES: + raise AppearanceError("Appearance archive expands beyond the allowed size") + if MANIFEST_NAME not in names: + raise AppearanceError(f"Appearance archive must contain {MANIFEST_NAME}") + raw_manifest = archive.read(MANIFEST_NAME) + if len(raw_manifest) > MAX_MANIFEST_BYTES: + raise AppearanceError("Appearance manifest is too large") + try: + manifest = json.loads(raw_manifest.decode("utf-8")) + except (UnicodeDecodeError, json.JSONDecodeError) as error: + raise AppearanceError(f"Appearance manifest is not valid UTF-8 JSON: {error}") from error + warnings = validate_manifest(manifest, registry) + if warning_sink is not None: + warning_sink.extend(warnings) + assets = declared_assets(manifest) + preview_asset_id = manifest.get("preview", {}).get("assetId") if is_record(manifest.get("preview")) else None + path_to_asset: dict[str, tuple[str, Mapping[str, Any]]] = {} + for asset_id, definition in assets.items(): + package_path = definition["source"]["path"] + if package_path in path_to_asset: + raise AppearanceError(f"Appearance assets cannot share package path: {package_path}") + path_to_asset[package_path] = (asset_id, definition) + allowed = {MANIFEST_NAME, *path_to_asset} + undeclared = sorted(set(names) - allowed) + if undeclared: + raise AppearanceError(f"Undeclared archive files: {', '.join(undeclared)}") + missing = sorted(set(path_to_asset) - set(names)) + if missing: + raise AppearanceError(f"Declared appearance assets are missing: {', '.join(missing)}") + total = len(raw_manifest) + for package_path, (asset_id, definition) in path_to_asset.items(): + data = archive.read(package_path) + total += len(data) + if total > MAX_EXPANDED_BYTES: + raise AppearanceError("Appearance archive expands beyond the allowed size") + assert_asset_limits(package_path, data, definition, asset_id == preview_asset_id) + digest = manifest.get("integrity", {}).get("sha256", {}).get(asset_id) if is_record(manifest.get("integrity")) else None + if digest and hashlib.sha256(data).hexdigest() != digest.lower(): + raise AppearanceError(f"Appearance asset integrity mismatch: {package_path}") + return manifest + except zipfile.BadZipFile as error: + raise AppearanceError(f"Appearance archive is not a valid ZIP: {error}") from error + + +def command_init(args: argparse.Namespace, registry: Mapping[str, Any]) -> None: + target = Path(args.project).resolve() + if target.exists() and any(target.iterdir()): + raise AppearanceError(f"Refusing to initialize non-empty directory: {target}") + target.mkdir(parents=True, exist_ok=True) + manifest = { + "schema": SCHEMA, + "schemaVersion": SCHEMA_VERSION, + "id": args.id, + "name": args.name, + "version": "1.0.0", + "mode": args.mode, + "requiredCapabilities": [], + "globals": {}, + "materials": {}, + "components": {}, + "scenes": {}, + "renderers": {}, + "assets": {}, + "integrity": {"sha256": {}}, + } + if args.author is not None: + manifest["author"] = args.author + if args.description is not None: + manifest["description"] = args.description + validate_manifest(manifest, registry) + (target / MANIFEST_NAME).write_bytes(manifest_bytes(manifest)) + (target / "assets").mkdir(exist_ok=True) + print(f"INITIALIZED: {target}") + print(f"Manifest: {target / MANIFEST_NAME}") + + +def command_build(args: argparse.Namespace, registry: Mapping[str, Any]) -> None: + project = Path(args.project).resolve() + manifest_path = project / MANIFEST_NAME + manifest = read_json(manifest_path) + assets = declared_assets(manifest) + if assets and "assets.v1" not in manifest.get("requiredCapabilities", []): + manifest.setdefault("requiredCapabilities", []).append("assets.v1") + if manifest.get("backgroundMedia") and "background-media.v1" not in manifest.get("requiredCapabilities", []): + manifest.setdefault("requiredCapabilities", []).append("background-media.v1") + validate_manifest(manifest, registry) + preview_asset_id = manifest.get("preview", {}).get("assetId") if is_record(manifest.get("preview")) else None + digests: dict[str, str] = {} + for asset_id, definition in assets.items(): + package_path = definition["source"]["path"] + file_path = project.joinpath(*PurePosixPath(package_path).parts).resolve() + if not file_path.is_file(): + raise AppearanceError(f"Declared appearance asset is missing: {package_path}") + data = file_path.read_bytes() + assert_asset_limits(package_path, data, definition, asset_id == preview_asset_id) + digests[asset_id] = hashlib.sha256(data).hexdigest() + manifest["integrity"] = {"sha256": digests} + manifest_path.write_bytes(manifest_bytes(manifest)) + validate_project(project, registry) + output = Path(args.output).resolve() if args.output else project.with_suffix(".bitfun-appearance") + if output.suffix != ".bitfun-appearance": + raise AppearanceError("Output filename must end in .bitfun-appearance") + output.parent.mkdir(parents=True, exist_ok=True) + if output.exists() and not args.force: + raise AppearanceError(f"Output already exists: {output}; pass --force") + entries = [(MANIFEST_NAME, manifest_path.read_bytes())] + video_paths = { + definition["source"]["path"] + for definition in assets.values() + if definition.get("kind") == "video" + } + for asset_id, definition in sorted(assets.items()): + package_path = definition["source"]["path"] + data = project.joinpath(*PurePosixPath(package_path).parts).read_bytes() + entries.append((package_path, data)) + with zipfile.ZipFile(output, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive: + for name, data in entries: + info = zipfile.ZipInfo(name, date_time=(1980, 1, 1, 0, 0, 0)) + info.compress_type = zipfile.ZIP_STORED if name in video_paths else zipfile.ZIP_DEFLATED + info.external_attr = 0o100644 << 16 + archive.writestr(info, data) + validate_archive(output, registry) + print(f"BUILT: {output}") + print(f"Archive bytes: {output.stat().st_size}; assets: {len(assets)}; integrity hashes: {len(digests)}") + + +def source_kind(path: Path) -> str: + if path.is_dir(): + return "project" + if path.is_file() and path.suffix == ".bitfun-appearance": + return "archive" + raise AppearanceError("Input must be an Appearance project directory or .bitfun-appearance archive") + + +def command_validate(args: argparse.Namespace, registry: Mapping[str, Any]) -> None: + source = Path(args.source).resolve() + kind = source_kind(source) + warnings: list[dict[str, str]] = [] + manifest = ( + validate_project(source, registry, warnings) + if kind == "project" + else validate_archive(source, registry, warnings) + ) + print(f"VALID: {source}") + print(f"Appearance: {manifest['name']} ({manifest['id']} {manifest['version']}, {manifest['mode']})") + print(f"Components: {len(manifest.get('components', {}))}; scenes: {len(manifest.get('scenes', {}))}; assets: {len(manifest.get('assets', {}))}") + if warnings: + print(f"Warnings ({len(warnings)}):") + print(format_issues(warnings)) + + +def collect_asset_roles(manifest: Mapping[str, Any]) -> dict[str, list[str]]: + roles = {asset_id: [] for asset_id in declared_assets(manifest)} + preview = manifest.get("preview") + if is_record(preview) and preview.get("kind") == "asset" and isinstance(preview.get("assetId"), str): + roles.setdefault(preview["assetId"], []).append("preview") + background_media = manifest.get("backgroundMedia") + if is_record(background_media): + if isinstance(background_media.get("assetId"), str): + roles.setdefault(background_media["assetId"], []).append("backgroundMedia.video") + if isinstance(background_media.get("posterAssetId"), str): + roles.setdefault(background_media["posterAssetId"], []).append("backgroundMedia.poster") + for path, style in iter_styles(manifest): + if is_record(style.get("backgroundImage")) and style["backgroundImage"].get("kind") == "asset": + roles.setdefault(style["backgroundImage"]["assetId"], []).append(f"{path}.backgroundImage") + if isinstance(style.get("backgroundImages"), list): + for index, item in enumerate(style["backgroundImages"]): + if is_record(item) and item.get("kind") == "asset": + roles.setdefault(item["assetId"], []).append(f"{path}.backgroundImages.{index}") + return roles + + +def inspect_manifest(manifest: Mapping[str, Any], source: Path) -> None: + summary = { + "source": str(source), + "registryCommit": load_registry().get("generatedFrom"), + "schema": manifest.get("schema"), + "schemaVersion": manifest.get("schemaVersion"), + "id": manifest.get("id"), + "name": manifest.get("name"), + "version": manifest.get("version"), + "mode": manifest.get("mode"), + "preview": manifest.get("preview"), + "backgroundMedia": manifest.get("backgroundMedia"), + "capabilities": manifest.get("requiredCapabilities", []), + "globalTokenCounts": {key: len(value) for key, value in manifest.get("globals", {}).items() if is_record(value)}, + "materials": sorted(manifest.get("materials", {})), + "components": {key: sorted(value.get("parts", {})) for key, value in manifest.get("components", {}).items() if is_record(value)}, + "scenes": {key: sorted(value.get("parts", {})) for key, value in manifest.get("scenes", {}).items() if is_record(value)}, + "renderers": sorted(manifest.get("renderers", {})), + "assets": {}, + } + roles = collect_asset_roles(manifest) + for asset_id, definition in declared_assets(manifest).items(): + summary["assets"][asset_id] = { + "path": definition["source"]["path"], + "mimeType": definition["mimeType"], + "roles": roles.get(asset_id, []), + "integrity": manifest.get("integrity", {}).get("sha256", {}).get(asset_id), + } + print(json.dumps(summary, ensure_ascii=False, indent=2)) + + +def command_inspect(args: argparse.Namespace, registry: Mapping[str, Any]) -> None: + source = Path(args.source).resolve() + kind = source_kind(source) + manifest = validate_project(source, registry) if kind == "project" else validate_archive(source, registry) + inspect_manifest(manifest, source) + + +def format_descriptor(descriptor: Mapping[str, Any], kind: str) -> dict[str, Any]: + return { + "kind": kind, + "id": descriptor.get("id"), + "parts": [ + { + "id": part.get("id"), + **({"allowedProperties": part.get("allowedProperties")} if part.get("allowedProperties") else {}), + **({"propertyProfile": part.get("propertyProfile")} if part.get("propertyProfile") else {}), + **({"forceableProperties": part.get("forceableProperties")} if part.get("forceableProperties") else {}), + **({"visualRole": part.get("visualRole")} if part.get("visualRole") else {}), + **({"continuityGroup": part.get("continuityGroup")} if part.get("continuityGroup") else {}), + } + for part in descriptor.get("parts", []) + ], + "facets": [ + {"id": facet.get("id"), "attribute": facet.get("attribute"), "values": facet.get("values", [])} + for facet in descriptor.get("facets", []) + ], + "states": [ + {"id": state.get("id"), "selector": state.get("selector")} + for state in descriptor.get("states", []) + ], + } + + +def command_contract(args: argparse.Namespace, registry: Mapping[str, Any]) -> None: + if args.action == "properties": + print(json.dumps(sorted(STYLE_PROPERTIES), indent=2)) + return + if args.action == "tokens": + key = "cssTokenNames" if args.token_kind == "css" else "widgetVariableNames" + print("\n".join(registry.get(key, []))) + return + if args.kind == "renderers": + values = registry.get("renderers", []) + if args.action == "list": + print("\n".join(values)) + return + raise AppearanceError("Renderer contracts are documented in references/renderer-contracts.md") + key = "components" if args.kind == "components" else "scenes" + descriptors = registry.get(key, []) + if args.action == "list": + term = (args.match or "").lower() + rows = [] + for descriptor in descriptors: + if term and term not in descriptor["id"].lower(): + continue + rows.append(f"{descriptor['id']}\tparts={len(descriptor.get('parts', []))}\tfacets={len(descriptor.get('facets', []))}\tstates={len(descriptor.get('states', []))}") + print("\n".join(rows)) + return + descriptor = next((item for item in descriptors if item.get("id") == args.id), None) + if descriptor is None: + raise AppearanceError(f"Unknown {args.kind[:-1]} contract: {args.id}") + print(json.dumps(format_descriptor(descriptor, args.kind[:-1]), ensure_ascii=False, indent=2)) + + +def create_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Build and validate BitFun .bitfun-appearance packages") + subparsers = parser.add_subparsers(dest="command", required=True) + init = subparsers.add_parser("init", help="initialize a minimal sparse Appearance project") + init.add_argument("project") + init.add_argument("--id", required=True) + init.add_argument("--name", required=True) + init.add_argument("--mode", choices=("light", "dark"), default="dark") + init.add_argument("--author") + init.add_argument("--description") + build = subparsers.add_parser("build", help="write integrity hashes and build a deterministic archive") + build.add_argument("project") + build.add_argument("--output") + build.add_argument("--force", action="store_true") + validate = subparsers.add_parser("validate", help="validate a project or archive") + validate.add_argument("source") + inspect = subparsers.add_parser("inspect", help="print a validated package summary and asset role map") + inspect.add_argument("source") + contract = subparsers.add_parser("contract", help="query the bundled production registry snapshot") + contract_sub = contract.add_subparsers(dest="action", required=True) + contract_list = contract_sub.add_parser("list") + contract_list.add_argument("kind", choices=("components", "scenes", "renderers")) + contract_list.add_argument("--match") + contract_show = contract_sub.add_parser("show") + contract_show.add_argument("kind", choices=("components", "scenes")) + contract_show.add_argument("id") + contract_tokens = contract_sub.add_parser("tokens") + contract_tokens.add_argument("token_kind", choices=("css", "widget")) + contract_sub.add_parser("properties") + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + args = create_parser().parse_args(argv) + try: + registry = load_registry() + if args.command == "init": + command_init(args, registry) + elif args.command == "build": + command_build(args, registry) + elif args.command == "validate": + command_validate(args, registry) + elif args.command == "inspect": + command_inspect(args, registry) + elif args.command == "contract": + command_contract(args, registry) + return 0 + except AppearanceError as error: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + except OSError as error: + print(f"ERROR: {error}", file=sys.stderr) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/build_support.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/build_support.py new file mode 100644 index 0000000000..d759c11bf1 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/build_support.py @@ -0,0 +1,211 @@ +from __future__ import annotations + +import hashlib +import json +import os +import shutil +import subprocess +import sys +import tempfile +import zipfile +from pathlib import Path +from typing import Any, Iterable, Mapping, Sequence + + +SKILL_ROOT = Path(__file__).resolve().parents[1] +APPEARANCE_TOOL = SKILL_ROOT / "scripts" / "bitfun_appearance.py" +HOST_VERIFY = SKILL_ROOT / "scripts" / "verify_host.py" +SYNC_REGISTRY = SKILL_ROOT / "scripts" / "sync_registry.py" +REGISTRY_PATH = SKILL_ROOT / "references" / "appearance-registry.json" +RUNTIME_CHECKLIST_SCHEMA = "bitfun.appearance.runtime-checklist" + + +class BuildSupportError(ValueError): + pass + + +def read_json(path: Path) -> dict[str, Any]: + try: + value = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as error: + raise BuildSupportError(f"Could not read JSON {path}: {error}") from error + if not isinstance(value, dict): + raise BuildSupportError(f"JSON root must be an object: {path}") + return value + + +def atomic_write_json(path: Path, value: Any) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + payload = json.dumps(value, ensure_ascii=False, indent=2) + "\n" + with tempfile.NamedTemporaryFile( + "w", encoding="utf-8", newline="\n", dir=path.parent, delete=False + ) as handle: + handle.write(payload) + temporary = Path(handle.name) + os.replace(temporary, path) + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def run(command: Sequence[str], cwd: Path = SKILL_ROOT) -> None: + subprocess.run(list(command), cwd=cwd, check=True) + + +def run_json(command: Sequence[str], cwd: Path = SKILL_ROOT) -> dict[str, Any]: + result = subprocess.run( + list(command), + cwd=cwd, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + if result.returncode != 0: + details = result.stderr.strip() or result.stdout.strip() or "command failed without diagnostic output" + raise BuildSupportError( + f"Command failed with exit code {result.returncode}: {' '.join(command)}\n{details}" + ) + try: + value = json.loads(result.stdout) + except json.JSONDecodeError as error: + raise BuildSupportError(f"Command did not return a JSON object: {' '.join(command)}") from error + if not isinstance(value, dict): + raise BuildSupportError(f"Command did not return a JSON object: {' '.join(command)}") + print(json.dumps(value, ensure_ascii=False, indent=2)) + return value + + +def copy_input(source: Path, destination: Path) -> None: + destination.parent.mkdir(parents=True, exist_ok=True) + if source.resolve() != destination.resolve(): + shutil.copy2(source, destination) + + +def initialize_package( + package: Path, + *, + appearance_id: str, + name: str, + mode: str, + force: bool, +) -> None: + manifest = package / "appearance.json" + if manifest.exists(): + if not force: + raise BuildSupportError(f"Manifest already exists; use --force to rebuild: {manifest}") + return + if package.exists() and any(package.iterdir()): + if not force: + raise BuildSupportError(f"Package directory is non-empty; use --force to recover: {package}") + return + run([ + sys.executable, + str(APPEARANCE_TOOL), + "init", + str(package), + "--id", + appearance_id, + "--name", + name, + "--mode", + mode, + ]) + + +def validate_project(package: Path) -> None: + run([sys.executable, str(APPEARANCE_TOOL), "validate", str(package)]) + + +def build_archive(package: Path, archive: Path) -> None: + command = [ + sys.executable, + str(APPEARANCE_TOOL), + "build", + str(package), + "--output", + str(archive), + ] + if archive.exists(): + command.append("--force") + run(command) + + +def validate_archive(archive: Path) -> None: + run([sys.executable, str(APPEARANCE_TOOL), "validate", str(archive)]) + + +def verify_host( + bitfun_repo: Path, + archive: Path, + *, + report: Path | None = None, + strict_warnings: bool = True, +) -> dict[str, Any] | None: + run([sys.executable, str(SYNC_REGISTRY), str(bitfun_repo), "--check"]) + command = [sys.executable, str(HOST_VERIFY), str(bitfun_repo), str(archive)] + if report is not None: + command.extend(["--report", str(report)]) + if strict_warnings: + command.append("--strict-warnings") + run(command) + return read_json(report) if report is not None else None + + +def registry_provenance() -> dict[str, Any]: + registry = read_json(REGISTRY_PATH) + return { + "sourceRevision": registry.get("sourceRevision"), + "sourceDirty": registry.get("sourceDirty"), + "generatedAt": registry.get("generatedAt"), + } + + +def ensure_runtime_checklist( + path: Path, + *, + appearance_id: str, + checks: Iterable[tuple[str, str]], +) -> Path: + if path.exists(): + return path + atomic_write_json(path, { + "schema": RUNTIME_CHECKLIST_SCHEMA, + "schemaVersion": 1, + "appearanceId": appearance_id, + "status": "pending", + "checks": [ + {"id": check_id, "label": label, "status": "pending", "evidence": []} + for check_id, label in checks + ], + }) + return path + + +def archive_member_compression(archive: Path, member: str) -> int: + with zipfile.ZipFile(archive) as bundle: + return bundle.getinfo(member).compress_type + + +def check_recorded_file( + root: Path, + entry: Mapping[str, Any], + *, + label: str, + issues: list[str], +) -> Path: + path_value = entry.get("path") + digest = entry.get("sha256") + if not isinstance(path_value, str) or not isinstance(digest, str): + issues.append(f"{label} record is invalid") + return root + path = root / path_value + if not path.is_file() or sha256(path) != digest: + issues.append(f"{label} hash mismatch") + return path diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/media_support.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/media_support.py new file mode 100644 index 0000000000..e115aa5170 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/media_support.py @@ -0,0 +1,526 @@ +from __future__ import annotations + +import argparse +import io +import json +import math +import re +import shutil +import subprocess +from pathlib import Path +from typing import Any, Sequence + +from PIL import Image, ImageDraw, ImageEnhance, ImageOps + + +DEFAULT_FRAME_DURATION_MS = 40 +MAX_IMAGE_BYTES = 16 * 1024 * 1024 +MAX_VIDEO_BYTES = 64 * 1024 * 1024 +MAX_PREVIEW_BYTES = 4 * 1024 * 1024 +MAX_VIDEO_DIMENSION = 4_096 +MAX_VIDEO_PIXELS = 9_000_000 +VIDEO_SUFFIXES = {".m4v", ".mkv", ".mov", ".mp4", ".webm"} +HEX_COLOR_PATTERN = re.compile(r"^#[0-9a-fA-F]{6}$") +VIDEO_QUALITY_MODES = {"auto", "lossless", "high", "balanced"} +STATIC_QUALITY_MODES = {"auto", "lossless", "quality"} +AUTO_LOSSLESS_PIXEL_SECONDS = 12_000_000 + + +class MediaSupportError(ValueError): + pass + + +def parse_crop(value: str) -> tuple[float, float, float, float]: + try: + left, top, right, bottom = (float(part.strip()) for part in value.split(",")) + except ValueError as exc: + raise argparse.ArgumentTypeError("crop must be left,top,right,bottom") from exc + if not (0 <= left < right <= 1 and 0 <= top < bottom <= 1): + raise argparse.ArgumentTypeError("crop coordinates must be normalized values in [0, 1]") + return left, top, right, bottom + + +def parse_size(value: str) -> tuple[int, int]: + try: + width_text, height_text = value.lower().split("x", 1) + width, height = int(width_text), int(height_text) + except ValueError as exc: + raise argparse.ArgumentTypeError("size must use WIDTHxHEIGHT") from exc + if width <= 0 or height <= 0: + raise argparse.ArgumentTypeError("size values must be positive") + return width, height + + +def parse_color(value: str) -> tuple[int, int, int]: + if not HEX_COLOR_PATTERN.fullmatch(value): + raise argparse.ArgumentTypeError("color must use #RRGGBB") + return int(value[1:3], 16), int(value[3:5], 16), int(value[5:7], 16) + + +def load_image_frames(source: Path, fallback_duration_ms: int) -> tuple[list[Image.Image], list[int], int]: + image = Image.open(source) + frame_count = getattr(image, "n_frames", 1) + frames: list[Image.Image] = [] + durations: list[int] = [] + for frame_index in range(frame_count): + image.seek(frame_index) + frames.append(image.convert("RGBA").copy()) + durations.append(image.info.get("duration") or fallback_duration_ms) + return frames, durations, image.info.get("loop", 0) + + +def probe_video_duration(source: Path) -> float: + ffprobe = shutil.which("ffprobe") + if not ffprobe: + raise RuntimeError("ffprobe is required to inspect video sources") + result = subprocess.run( + [ffprobe, "-v", "error", "-show_entries", "format=duration", "-of", "json", str(source)], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + if result.returncode != 0: + raise RuntimeError(result.stderr.strip() or "ffprobe failed to inspect the video source") + try: + duration = float(json.loads(result.stdout)["format"]["duration"]) + except (KeyError, TypeError, ValueError, json.JSONDecodeError) as exc: + raise RuntimeError("ffprobe did not report a valid video duration") from exc + if not math.isfinite(duration) or duration <= 0: + raise MediaSupportError("Video duration must be a positive finite number") + return duration + + +def probe_video(path: Path) -> dict[str, Any]: + ffprobe = shutil.which("ffprobe") + if not ffprobe: + raise RuntimeError("ffprobe is required to inspect the generated background video") + result = subprocess.run( + [ + ffprobe, + "-v", + "error", + "-select_streams", + "v:0", + "-show_entries", + ( + "format=duration,size:" + "stream=codec_name,width,height,r_frame_rate,pix_fmt,sample_aspect_ratio,display_aspect_ratio" + ), + "-of", + "json", + str(path), + ], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=True, + ) + payload = json.loads(result.stdout) + stream = payload["streams"][0] + width = int(stream["width"]) + height = int(stream["height"]) + sample_aspect_ratio = stream.get("sample_aspect_ratio") or "1:1" + try: + numerator_text, denominator_text = sample_aspect_ratio.split(":", 1) + numerator, denominator = int(numerator_text), int(denominator_text) + if numerator <= 0 or denominator <= 0: + raise ValueError("sample aspect ratio must be positive") + pixel_aspect_ratio = numerator / denominator + except (AttributeError, TypeError, ValueError, ZeroDivisionError): + pixel_aspect_ratio = 1.0 + sample_aspect_ratio = "1:1" + display_width = max(1, round(width * pixel_aspect_ratio)) + return { + "codec": stream["codec_name"], + "width": width, + "height": height, + "displayWidth": display_width, + "displayHeight": height, + "sampleAspectRatio": sample_aspect_ratio, + "displayAspectRatio": stream.get("display_aspect_ratio") or f"{display_width}:{height}", + "frameRate": stream["r_frame_rate"], + "pixelFormat": stream["pix_fmt"], + "durationSeconds": round(float(payload["format"]["duration"]), 3), + "bytes": int(payload["format"]["size"]), + } + + +def validate_host_video(video: dict[str, Any], *, codec: str | None = "vp9") -> None: + if codec is not None and video["codec"] != codec: + raise MediaSupportError(f"Background video must use {codec.upper()}, found {video['codec']}") + display_width = int(video.get("displayWidth", video["width"])) + display_height = int(video.get("displayHeight", video["height"])) + if display_width <= 0 or display_height <= 0: + raise MediaSupportError("Background video display dimensions must be positive") + if display_width > MAX_VIDEO_DIMENSION or display_height > MAX_VIDEO_DIMENSION: + raise MediaSupportError( + f"Background video display dimensions {display_width}x{display_height} " + f"exceed {MAX_VIDEO_DIMENSION} pixels" + ) + if display_width * display_height > MAX_VIDEO_PIXELS: + raise MediaSupportError( + f"Background video display dimensions {display_width}x{display_height} exceed {MAX_VIDEO_PIXELS} pixels" + ) + if video["durationSeconds"] > 60: + raise MediaSupportError("Background video exceeds 60 seconds") + if video["bytes"] > MAX_VIDEO_BYTES: + raise MediaSupportError("Background video exceeds 64 MiB") + + +def extract_video_frame(source: Path, time_seconds: float, *, contact_frame: bool = False) -> Image.Image: + ffmpeg = shutil.which("ffmpeg") + if not ffmpeg: + raise RuntimeError("ffmpeg is required to extract video frames") + last_error = "ffmpeg failed to extract a frame" + seek_times = [max(0, time_seconds - offset) for offset in (0, 0.05, 0.1, 0.25)] + for seek_time in dict.fromkeys(seek_times): + for fast_seek in (True, False): + command = [ffmpeg, "-hide_banner", "-loglevel", "error"] + if fast_seek: + command.extend(["-ss", f"{seek_time:.6f}"]) + command.extend(["-i", str(source)]) + if not fast_seek: + command.extend(["-ss", f"{seek_time:.6f}"]) + command.extend(["-map", "0:v:0", "-frames:v", "1"]) + filters = [ + "scale=w='max(2,round(iw*sar/2)*2)':h='max(2,round(ih/2)*2)'", + "setsar=1", + ] + if contact_frame: + filters.append("scale=640:360:force_original_aspect_ratio=decrease") + command.extend(["-vf", ",".join(filters)]) + command.extend(["-f", "image2pipe", "-vcodec", "png", "-"]) + result = subprocess.run(command, capture_output=True, check=False) + if result.returncode != 0: + last_error = result.stderr.decode("utf-8", errors="replace").strip() or last_error + continue + try: + with Image.open(io.BytesIO(result.stdout)) as image: + return image.convert("RGBA").copy() + except OSError: + last_error = "ffmpeg returned an invalid frame image" + raise RuntimeError(last_error) + + +def load_video_contact_frames( + source: Path, + duration: float, + *, + frame_count: int = 12, +) -> tuple[list[Image.Image], list[str]]: + times = [duration * (index + 0.5) / frame_count for index in range(frame_count)] + return ( + [extract_video_frame(source, time_seconds, contact_frame=True) for time_seconds in times], + [f"{time_seconds:.2f}s" for time_seconds in times], + ) + + +def normalized_crop(image: Image.Image, crop: tuple[float, float, float, float]) -> Image.Image: + width, height = image.size + left, top, right, bottom = crop + return image.crop((round(width * left), round(height * top), round(width * right), round(height * bottom))) + + +def fit_crop( + image: Image.Image, + crop: tuple[float, float, float, float], + size: tuple[int, int], +) -> Image.Image: + return ImageOps.fit( + normalized_crop(image, crop), + size, + method=Image.Resampling.LANCZOS, + centering=(0.5, 0.5), + ) + + +def darken(image: Image.Image, brightness: float, tint: tuple[int, int, int, int]) -> Image.Image: + darkened = ImageEnhance.Brightness(image.convert("RGBA")).enhance(brightness) + return Image.alpha_composite(darkened, Image.new("RGBA", darkened.size, tint)) + + +def _encode_vp9_attempt(source: Path, output: Path, *, crf: int | None, lossless: bool) -> None: + ffmpeg = shutil.which("ffmpeg") + if not ffmpeg: + raise RuntimeError("ffmpeg is required to encode the host-managed WebM background") + max_width = "if(gte(dar,1),3840,2160)" + max_height = "if(gte(dar,1),2160,3840)" + scale_factor = f"min(1,min({max_width}/(iw*sar),{max_height}/ih))" + scale = ( + f"scale=w='max(2,round(iw*sar*{scale_factor}/2)*2)':" + f"h='max(2,round(ih*{scale_factor}/2)*2)',setsar=1,fps=30" + ) + command = [ + ffmpeg, + "-hide_banner", + "-loglevel", + "error", + "-y", + "-i", + str(source), + "-t", + "60", + "-an", + "-map_metadata", + "-1", + "-vf", + scale, + "-c:v", + "libvpx-vp9", + "-b:v", + "0", + "-pix_fmt", + "yuv420p", + "-row-mt", + "1", + "-deadline", + "good", + ] + if lossless: + command.extend(["-lossless", "1"]) + else: + command.extend(["-crf", str(crf)]) + command.append(str(output)) + result = subprocess.run( + command, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + if result.returncode != 0: + raise RuntimeError(result.stderr.strip() or "ffmpeg failed to encode the WebM background") + + +def _source_pixel_seconds(source: Path) -> float | None: + try: + info = probe_video(source) + return ( + min(float(info["durationSeconds"]), 60) + * int(info["displayWidth"]) + * int(info["displayHeight"]) + ) + except (KeyError, IndexError, OSError, RuntimeError, subprocess.CalledProcessError, json.JSONDecodeError): + return None + + +def encode_vp9_webm( + source: Path, + output: Path, + *, + quality: str = "auto", + crf: int | None = None, +) -> dict[str, Any]: + if quality not in VIDEO_QUALITY_MODES: + raise MediaSupportError(f"Unknown video quality mode: {quality}") + if crf is not None and not 0 <= crf <= 63: + raise MediaSupportError("Video CRF must be between 0 and 63") + + attempts: list[dict[str, Any]] = [] + + def attempt(*, attempt_crf: int | None, lossless: bool) -> dict[str, Any] | None: + _encode_vp9_attempt(source, output, crf=attempt_crf, lossless=lossless) + size = output.stat().st_size + attempts.append({"codecLossless": lossless, "crf": attempt_crf, "bytes": size}) + if size > MAX_VIDEO_BYTES: + return None + video = probe_video(output) + validate_host_video(video) + return { + "policy": "custom-crf" if crf is not None else quality, + "codecLossless": lossless, + "crf": attempt_crf, + "attempts": attempts, + "hostTransform": { + "maxDurationSeconds": 60, + "maxLandscapeSize": [3840, 2160], + "maxPortraitSize": [2160, 3840], + "framesPerSecond": 30, + "pixelFormat": "yuv420p", + "sampleAspectRatio": "1:1", + "audio": "removed", + "metadata": "removed", + }, + "video": video, + } + + if crf is not None: + result = attempt(attempt_crf=crf, lossless=False) + if result is not None: + return result + raise MediaSupportError( + f"VP9 CRF {crf} produced {output.stat().st_size} bytes, above the {MAX_VIDEO_BYTES}-byte limit" + ) + + if quality == "lossless": + result = attempt(attempt_crf=None, lossless=True) + if result is not None: + return result + raise MediaSupportError( + f"Lossless VP9 produced {output.stat().st_size} bytes, above the {MAX_VIDEO_BYTES}-byte limit" + ) + + if quality in {"high", "balanced"}: + selected_crf = 20 if quality == "high" else 32 + result = attempt(attempt_crf=selected_crf, lossless=False) + if result is not None: + return result + raise MediaSupportError( + f"Video quality mode {quality} produced {output.stat().st_size} bytes; use auto or a higher --video-crf" + ) + + pixel_seconds = _source_pixel_seconds(source) + if pixel_seconds is not None and pixel_seconds <= AUTO_LOSSLESS_PIXEL_SECONDS: + result = attempt(attempt_crf=None, lossless=True) + if result is not None: + return result + + selected_crf = 20 + for _ in range(6): + result = attempt(attempt_crf=selected_crf, lossless=False) + if result is not None: + return result + ratio = max(output.stat().st_size / MAX_VIDEO_BYTES, 1.01) + step = max(4, math.ceil(6 * math.log2(ratio))) + next_crf = min(63, selected_crf + step) + if next_crf == selected_crf: + break + selected_crf = next_crf + raise MediaSupportError( + f"Automatic VP9 encoding could not satisfy the {MAX_VIDEO_BYTES}-byte limit; attempts: {attempts}" + ) + + +def save_webp(image: Image.Image, output: Path, quality: int) -> None: + image.convert("RGB").save(output, format="WEBP", quality=quality, method=6) + + +def encode_webp( + image: Image.Image, + output: Path, + *, + mode: str = "auto", + quality: int | None = None, + max_bytes: int = MAX_IMAGE_BYTES, +) -> dict[str, Any]: + if mode not in STATIC_QUALITY_MODES: + raise MediaSupportError(f"Unknown static quality mode: {mode}") + if quality is not None and not 1 <= quality <= 100: + raise MediaSupportError("Static quality must be between 1 and 100") + attempts: list[dict[str, Any]] = [] + + def attempt_lossless() -> dict[str, Any] | None: + image.convert("RGB").save(output, format="WEBP", lossless=True, method=6) + size = output.stat().st_size + attempts.append({"lossless": True, "quality": None, "bytes": size}) + if size <= max_bytes: + return {"lossless": True, "quality": None, "bytes": size, "attempts": attempts} + return None + + def attempt_quality(selected_quality: int) -> dict[str, Any] | None: + save_webp(image, output, selected_quality) + size = output.stat().st_size + attempts.append({"lossless": False, "quality": selected_quality, "bytes": size}) + if size <= max_bytes: + return {"lossless": False, "quality": selected_quality, "bytes": size, "attempts": attempts} + return None + + if quality is not None: + result = attempt_quality(quality) + if result is not None: + return result + raise MediaSupportError(f"WebP quality {quality} exceeds the {max_bytes}-byte asset limit") + if mode == "lossless": + result = attempt_lossless() + if result is not None: + return result + raise MediaSupportError(f"Lossless WebP exceeds the {max_bytes}-byte asset limit") + if mode == "quality": + result = attempt_quality(92) + if result is not None: + return result + raise MediaSupportError(f"WebP quality mode exceeds the {max_bytes}-byte asset limit") + + result = attempt_lossless() + if result is not None: + return result + for selected_quality in (95, 92, 88, 84, 80, 75, 70): + result = attempt_quality(selected_quality) + if result is not None: + return result + raise MediaSupportError(f"Automatic WebP encoding could not satisfy the {max_bytes}-byte asset limit") + + +def create_contact_sheet( + frames: Sequence[Image.Image], + output: Path, + *, + labels: Sequence[str] | None = None, + columns: int = 4, + rows: int = 3, + tile_size: tuple[int, int] = (320, 180), +) -> None: + if not frames: + raise MediaSupportError("Contact sheet requires at least one frame") + tile_count = columns * rows + indexes = [round(index * (len(frames) - 1) / (tile_count - 1)) for index in range(tile_count)] + sheet = Image.new("RGB", (columns * tile_size[0], rows * tile_size[1]), "#08111f") + draw = ImageDraw.Draw(sheet) + for tile_index, frame_index in enumerate(indexes): + frame = ImageOps.fit(frames[frame_index].convert("RGB"), tile_size) + x = (tile_index % columns) * tile_size[0] + y = (tile_index // columns) * tile_size[1] + sheet.paste(frame, (x, y)) + label = labels[frame_index] if labels else f"frame {frame_index}" + draw.rectangle((x + 8, y + 8, x + 94, y + 30), fill=(4, 10, 20)) + draw.text((x + 14, y + 11), label, fill=(225, 240, 248)) + sheet.save(output, format="PNG", optimize=True) + + +def create_asset_preview_sheet( + assets: Path, + output: Path, + names: Sequence[str], + *, + columns: int = 3, +) -> None: + tile_width, tile_height = 360, 250 + content_size = (332, 202) + rows = math.ceil(len(names) / columns) + sheet = Image.new("RGB", (tile_width * columns, tile_height * rows), "#08111f") + draw = ImageDraw.Draw(sheet) + for index, name in enumerate(names): + path = assets / name + if not path.is_file(): + raise FileNotFoundError(f"Missing preview asset: {path}") + with Image.open(path) as image: + preview = ImageOps.contain(image.convert("RGB"), content_size, Image.Resampling.LANCZOS) + column, row = index % columns, index // columns + tile_x, tile_y = column * tile_width, row * tile_height + image_x = tile_x + (tile_width - preview.width) // 2 + image_y = tile_y + 38 + (content_size[1] - preview.height) // 2 + sheet.paste(preview, (image_x, image_y)) + draw.text((tile_x + 14, tile_y + 12), name, fill=(225, 240, 248)) + sheet.save(output, format="PNG", optimize=True) + + +def inspect_assets(assets: Path) -> dict[str, object]: + result: dict[str, object] = {} + for path in sorted(assets.glob("*.webp")): + with Image.open(path) as image: + result[path.name] = { + "bytes": path.stat().st_size, + "size": list(image.size), + "frames": getattr(image, "n_frames", 1), + "withinAssetLimit": path.stat().st_size <= MAX_IMAGE_BYTES, + } + for path in sorted(assets.glob("*.webm")): + result[path.name] = { + "bytes": path.stat().st_size, + "withinAssetLimit": path.stat().st_size <= MAX_VIDEO_BYTES, + } + return result diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/sync_registry.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/sync_registry.py new file mode 100644 index 0000000000..f4c67a6ae6 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/sync_registry.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +"""Export the production BitFun Appearance registry into this standalone skill.""" + +from __future__ import annotations + +import argparse +import datetime as dt +import hashlib +import json +import subprocess +import sys +from pathlib import Path +from typing import Any + + +DEFAULT_OUTPUT = Path(__file__).resolve().parent.parent / "references" / "appearance-registry.json" + + +class SyncError(Exception): + pass + + +def run(command: list[str], cwd: Path) -> str: + result = subprocess.run(command, cwd=cwd, capture_output=True, text=True, encoding="utf-8", errors="replace", check=False) + if result.returncode != 0: + raise SyncError(result.stderr.strip() or result.stdout.strip() or f"Command failed: {' '.join(command)}") + return result.stdout.strip() + + +def export_registry(repo: Path) -> dict[str, Any]: + web_ui = repo / "src" / "web-ui" + if not (web_ui / "src" / "infrastructure" / "appearance" / "registry" / "defaultAppearanceRegistry.ts").is_file(): + raise SyncError(f"Not a BitFun checkout with the Appearance registry: {repo}") + source = r""" +import { createServer } from 'vite'; +const server = await createServer({ root: process.cwd(), logLevel: 'silent', appType: 'custom', server: { middlewareMode: true } }); +try { + const registryModule = await server.ssrLoadModule('/src/infrastructure/appearance/registry/defaultAppearanceRegistry.ts'); + const catalog = await server.ssrLoadModule('/src/infrastructure/appearance/builtins/catalog.ts'); + const widget = await server.ssrLoadModule('/src/infrastructure/appearance/adapters/widgetAppearanceVariables.ts'); + const profiles = await server.ssrLoadModule('/src/infrastructure/appearance/appearancePropertyProfiles.ts'); + const registry = registryModule.createDefaultAppearanceRegistry(); + const defaultForceableProperties = [...profiles.getDefaultForceableAppearanceProperties()]; + const differsFromDefaultForceable = values => values + && (values.length !== defaultForceableProperties.length + || values.some((value, index) => value !== defaultForceableProperties[index])); + const descriptor = item => ({ + id: item.id, + parts: item.parts.map(part => ({ + id: part.id, + ...(part.allowedProperties ? { allowedProperties: [...part.allowedProperties] } : {}), + ...(part.propertyProfile ? { propertyProfile: part.propertyProfile } : {}), + ...(differsFromDefaultForceable(part.forceableProperties) ? { forceableProperties: [...part.forceableProperties] } : {}), + ...(part.visualRole ? { visualRole: part.visualRole } : {}), + ...(part.continuityGroup ? { continuityGroup: part.continuityGroup } : {}), + })), + facets: (item.facets ?? []).map(facet => ({ id: facet.id, attribute: facet.attribute, values: [...facet.values] })), + states: (item.states ?? []).map(state => ({ id: state.id, selector: state.selector })), + }); + process.stdout.write(JSON.stringify({ + components: registry.getComponents().map(descriptor), + scenes: registry.getScenes().map(descriptor), + renderers: registry.getRendererAdapters().map(adapter => adapter.id), + defaultForceableProperties, + cssTokenNames: [...catalog.APPEARANCE_CSS_TOKEN_NAMES], + widgetVariableNames: [...widget.WIDGET_APPEARANCE_VARIABLE_NAMES], + })); +} finally { + await server.close(); +} +""" + raw = run(["node", "--input-type=module", "--eval", source], web_ui) + try: + return json.loads(raw) + except json.JSONDecodeError as error: + raise SyncError(f"Registry exporter produced invalid JSON: {error}\n{raw[:500]}") from error + + +def create_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Synchronize the standalone registry from a BitFun checkout") + parser.add_argument("repo", help="BitFun repository root") + parser.add_argument("--output", default=str(DEFAULT_OUTPUT)) + parser.add_argument("--allow-dirty", action="store_true", help="record and export a dirty working tree") + parser.add_argument("--check", action="store_true", help="fail when the bundled registry differs from the checkout") + return parser + + +def main() -> int: + args = create_parser().parse_args() + try: + repo = Path(args.repo).resolve() + revision = run(["git", "rev-parse", "HEAD"], repo) + status = run(["git", "status", "--porcelain=v1"], repo) + dirty = bool(status) + if dirty and not args.allow_dirty: + raise SyncError("BitFun checkout is dirty; commit changes or pass --allow-dirty so provenance records the state") + tree_evidence = run(["git", "diff", "--no-ext-diff", "HEAD"], repo) + "\n" + status + snapshot = export_registry(repo) + output_value = { + "schema": "bitfun.appearance.registry", + "schemaVersion": 1, + "generatedFrom": revision, + "generatedAt": dt.datetime.now(dt.timezone.utc).isoformat(), + "sourceRevision": revision, + "sourceDirty": dirty, + "sourceTreeHash": hashlib.sha256(tree_evidence.encode("utf-8")).hexdigest(), + **snapshot, + } + output = Path(args.output).resolve() + if args.check: + if not output.is_file(): + raise SyncError(f"Bundled registry is missing: {output}") + try: + current = json.loads(output.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as error: + raise SyncError(f"Could not read bundled registry: {error}") from error + comparable_expected = {key: value for key, value in output_value.items() if key != "generatedAt"} + comparable_current = {key: value for key, value in current.items() if key != "generatedAt"} + if comparable_current != comparable_expected: + raise SyncError( + "Bundled registry differs from the selected BitFun checkout; " + "run sync_registry.py without --check to refresh it" + ) + print(json.dumps({ + "output": str(output), + "revision": revision, + "dirty": dirty, + "synchronized": True, + }, indent=2)) + return 0 + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(json.dumps(output_value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(json.dumps({ + "output": str(output), + "revision": revision, + "dirty": dirty, + "components": len(snapshot["components"]), + "scenes": len(snapshot["scenes"]), + "renderers": len(snapshot["renderers"]), + }, indent=2)) + return 0 + except (SyncError, OSError, ValueError) as error: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/test_bitfun_appearance.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/test_bitfun_appearance.py new file mode 100644 index 0000000000..c1723b6921 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/test_bitfun_appearance.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import unittest + +import bitfun_appearance as appearance + + +def base_manifest() -> dict[str, object]: + return { + "schema": "bitfun.appearance", + "schemaVersion": 1, + "id": "validator-test", + "name": "Validator Test", + "version": "1.0.0", + "mode": "dark", + } + + +class ManifestValidatorTests(unittest.TestCase): + def setUp(self) -> None: + self.registry = appearance.load_registry() + + def validate(self, manifest: dict[str, object]) -> appearance.ManifestValidator: + validator = appearance.ManifestValidator(self.registry) + validator.validate(manifest) + return validator + + def test_unknown_and_circular_references_are_rejected(self) -> None: + unknown = base_manifest() + unknown["globals"] = { + "colors": {"accent": {"kind": "ref", "path": "globals.colors.missing"}}, + } + self.assertIn( + "UNKNOWN_TOKEN_REFERENCE", + {issue["code"] for issue in self.validate(unknown).errors}, + ) + + circular = base_manifest() + circular["globals"] = { + "colors": { + "first": {"kind": "ref", "path": "globals.colors.second"}, + "second": {"kind": "ref", "path": "globals.colors.first"}, + }, + } + self.assertIn( + "CIRCULAR_TOKEN_REFERENCE", + {issue["code"] for issue in self.validate(circular).errors}, + ) + + def test_reference_type_mismatch_is_rejected(self) -> None: + manifest = base_manifest() + manifest["globals"] = { + "colors": {"accent": {"kind": "hex", "value": "#ffffff"}}, + "lengths": {"spacing": {"kind": "ref", "path": "globals.colors.accent"}}, + } + self.assertIn( + "REFERENCE_TYPE_MISMATCH", + {issue["code"] for issue in self.validate(manifest).errors}, + ) + + def test_normalization_and_visual_semantic_warnings_are_reported(self) -> None: + manifest = base_manifest() + manifest["components"] = { + "button": { + "parts": { + "root": { + "base": {"borderStyle": "solid"}, + }, + }, + }, + "modal": { + "parts": { + "headerShell": { + "base": {"borderRadius": {"kind": "px", "value": 4}}, + }, + }, + }, + } + validator = self.validate(manifest) + self.assertEqual([], validator.errors) + codes = {issue["code"] for issue in validator.warnings} + self.assertIn("BORDER_WIDTH_NORMALIZED", codes) + self.assertIn("CONTINUOUS_SURFACE_FRAMED", codes) + + def test_excessive_override_usage_is_reported(self) -> None: + manifest = base_manifest() + manifest["components"] = { + "card": { + "parts": { + part: {"cascade": "override"} + for part in ("root", "header", "body", "footer") + }, + }, + } + validator = self.validate(manifest) + self.assertEqual([], validator.errors) + self.assertIn( + "EXCESSIVE_OVERRIDE_USAGE", + {issue["code"] for issue in validator.warnings}, + ) + + def test_contract_output_preserves_authoring_metadata(self) -> None: + modal = next(item for item in self.registry["components"] if item["id"] == "modal") + formatted = appearance.format_descriptor(modal, "component") + header = next(part for part in formatted["parts"] if part["id"] == "headerShell") + self.assertEqual("container", header["propertyProfile"]) + self.assertEqual("modal-dialog", header["continuityGroup"]) + button = next(item for item in self.registry["components"] if item["id"] == "button") + formatted_button = appearance.format_descriptor(button, "component") + self.assertTrue(formatted_button["states"]) + self.assertTrue(all("selector" in state for state in formatted_button["states"])) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/test_cinematic_tools.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/test_cinematic_tools.py new file mode 100644 index 0000000000..1d69cda7f4 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/test_cinematic_tools.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import json +import shutil +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + + +SKILL_ROOT = Path(__file__).resolve().parents[1] +EXAMPLE_ROOT = SKILL_ROOT / "examples" / "cinematic-animated-wallpaper" +EXAMPLE_SCRIPTS = EXAMPLE_ROOT / "scripts" +sys.path.insert(0, str(SKILL_ROOT / "scripts")) +sys.path.insert(0, str(EXAMPLE_SCRIPTS)) + +import build_support +import build_cinematic_skin as pipeline +import cinematic_recipe as contract +import media_support +import verify_host as host_verifier + + +class CinematicContractTests(unittest.TestCase): + def test_default_manifest_is_current_and_warning_free(self) -> None: + palette = contract.load_palette() + plan = contract.load_surface_plan() + manifest = contract.build_manifest( + appearance_id="cinematic-contract-test", + name="Cinematic Contract Test", + version="1.0.0", + mode="dark", + palette=palette, + surface_plan=plan, + ) + self.assertEqual([], contract.validate_manifest(manifest)) + self.assertIn("flow-chat-turn-rail", manifest["components"]) + self.assertIn("copyable-text-preview", manifest["components"]) + self.assertNotIn("input", manifest["components"]["toolbar-mode"]["parts"]) + self.assertNotIn("sessionMenu", manifest["components"]["toolbar-mode"]["parts"]) + self.assertNotIn("inputBar", manifest["components"]["floating-mini-chat"]["parts"]) + + def test_palette_requires_exact_semantic_keys_and_hex_values(self) -> None: + palette = contract.load_palette() + with tempfile.TemporaryDirectory() as temporary: + path = Path(temporary) / "palette.json" + broken = json.loads(json.dumps(palette)) + broken["colors"].pop("accent") + contract.atomic_write_json(path, broken) + with self.assertRaisesRegex(contract.CinematicContractError, "missing: accent"): + contract.load_palette(path) + + broken = json.loads(json.dumps(palette)) + broken["colors"]["accent"] = "rgba(1, 2, 3, 1)" + contract.atomic_write_json(path, broken) + with self.assertRaisesRegex(contract.CinematicContractError, "six-digit hex"): + contract.load_palette(path) + + def test_surface_plan_contains_only_resolvable_palette_placeholders(self) -> None: + palette = contract.load_palette() + plan = contract.load_surface_plan() + self.assertEqual("example-style-selection", plan["scope"]) + self.assertEqual("cinematic-animated-wallpaper", plan["styleId"]) + self.assertIsInstance(plan["validatedAgainstRegistryRevision"], str) + resolved = contract.resolve_palette_values(plan, palette["colors"]) + serialized = json.dumps(resolved) + self.assertNotIn('"kind": "palette"', serialized) + self.assertEqual(43, len(plan["components"])) + self.assertEqual(7, len(plan["scenes"])) + + def test_generic_support_validates_media_and_recorded_hashes(self) -> None: + self.assertEqual((0.1, 0.2, 0.9, 1.0), media_support.parse_crop("0.1,0.2,0.9,1")) + media_support.validate_host_video({ + "codec": "vp9", + "width": 1920, + "height": 1080, + "durationSeconds": 12, + "bytes": 1024, + }) + with self.assertRaisesRegex(media_support.MediaSupportError, "5160x2160"): + media_support.validate_host_video({ + "codec": "vp9", + "width": 3840, + "height": 2160, + "displayWidth": 5160, + "displayHeight": 2160, + "durationSeconds": 12, + "bytes": 1024, + }) + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + recorded = root / "recorded.txt" + recorded.write_text("stable", encoding="utf-8") + issues: list[str] = [] + build_support.check_recorded_file( + root, + {"path": "recorded.txt", "sha256": build_support.sha256(recorded)}, + label="fixture", + issues=issues, + ) + self.assertEqual([], issues) + + def test_build_record_requires_current_schema(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + output = Path(temporary) + build_support.atomic_write_json(output / "skin-build.json", { + "schema": "bitfun.appearance.unsupported-build", + "schemaVersion": 1, + }) + with self.assertRaisesRegex(contract.CinematicContractError, "Unsupported cinematic build config"): + pipeline.options_from_config(output, None) + + @unittest.skipUnless(shutil.which("ffmpeg") and shutil.which("ffprobe"), "ffmpeg is required") + def test_complete_build_and_check_pipeline(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + source = root / "fixture.mp4" + output = root / "skin" + subprocess.run( + [ + "ffmpeg", + "-hide_banner", + "-loglevel", + "error", + "-y", + "-f", + "lavfi", + "-i", + "color=c=0x7f1d1d:s=320x180:r=10", + "-t", + "1", + "-vf", + "setsar=43/32", + "-c:v", + "libx264", + "-pix_fmt", + "yuv420p", + str(source), + ], + check=True, + ) + self.assertEqual((430, 180), media_support.extract_video_frame(source, 0.5).size) + build_script = EXAMPLE_SCRIPTS / "build_cinematic_skin.py" + subprocess.run( + [ + sys.executable, + "-B", + str(build_script), + "build", + "--source", + str(source), + "--output", + str(output), + "--id", + "pipeline-fixture", + "--name", + "Pipeline Fixture", + "--frame-time", + "0.5", + ], + cwd=SKILL_ROOT, + check=True, + ) + config_path = output / "skin-build.json" + config = json.loads(config_path.read_text(encoding="utf-8")) + self.assertEqual("bitfun.appearance.recipe-build", config["schema"]) + self.assertEqual("cinematic-animated-wallpaper", config["recipe"]["id"]) + self.assertEqual("sources/surface-plan.json", config["inputs"]["surfacePlan"]["path"]) + self.assertEqual("auto", config["assetBuild"]["videoQuality"]) + self.assertEqual("auto", config["assetBuild"]["staticQualityMode"]) + resolved = config["assetBuild"]["resolvedEncoding"] + self.assertTrue(resolved["video"]["codecLossless"]) + video = resolved["video"]["video"] + self.assertEqual("1:1", video["sampleAspectRatio"]) + self.assertEqual(video["width"], video["displayWidth"]) + self.assertEqual(video["height"], video["displayHeight"]) + self.assertEqual(430, video["width"]) + self.assertEqual(180, video["height"]) + self.assertTrue(all(entry["lossless"] for entry in resolved["static"].values())) + archive = output / "pipeline-fixture.bitfun-appearance" + manifest = json.loads((output / "package" / "appearance.json").read_text(encoding="utf-8")) + host_video = host_verifier.verify_video_assets(archive, manifest)["background"] + self.assertEqual("1:1", host_video["sampleAspectRatio"]) + self.assertEqual(host_video["width"], host_video["displayWidth"]) + + subprocess.run( + [ + sys.executable, + "-B", + str(build_script), + "rebuild", + "--output", + str(output), + ], + cwd=SKILL_ROOT, + check=True, + ) + subprocess.run( + [ + sys.executable, + "-B", + str(build_script), + "check", + "--output", + str(output), + "--skip-host-verify", + ], + cwd=SKILL_ROOT, + check=True, + ) + config = json.loads(config_path.read_text(encoding="utf-8")) + self.assertEqual("bitfun.appearance.recipe-build", config["schema"]) + self.assertTrue(config["verification"]["projectValidation"]) + self.assertTrue(config["verification"]["archiveValidation"]) + self.assertFalse(config["verification"]["runtimeVisualInspection"]) + self.assertTrue((output / "asset-preview-sheet.png").is_file()) + self.assertTrue((output / "pipeline-fixture.bitfun-appearance").is_file()) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/verify_host.py b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/verify_host.py new file mode 100644 index 0000000000..6a5b22ddc0 --- /dev/null +++ b/src/crates/assembly/core/builtin_skills/create-bitfun-skin/scripts/verify_host.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +"""Validate and compile an Appearance manifest with a real BitFun checkout.""" + +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +import tempfile +import zipfile +from pathlib import Path +from typing import Any, Sequence + +import bitfun_appearance as appearance +import media_support +import sync_registry as registry_sync + + +class HostVerificationError(Exception): + pass + + +def run(command: list[str], cwd: Path) -> str: + result = subprocess.run( + command, + cwd=cwd, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + check=False, + ) + if result.returncode != 0: + raise HostVerificationError( + result.stderr.strip() or result.stdout.strip() or f"Command failed: {' '.join(command)}" + ) + return result.stdout.strip() + + +def verify_video_assets(source: Path, manifest: dict[str, Any]) -> dict[str, dict[str, Any]]: + video_assets = { + asset_id: definition + for asset_id, definition in (manifest.get("assets") or {}).items() + if definition.get("kind") == "video" + } + if not video_assets: + return {} + + kind = appearance.source_kind(source) + results: dict[str, dict[str, Any]] = {} + try: + with tempfile.TemporaryDirectory(prefix="bitfun-host-media-") as temporary: + root = Path(temporary) + archive = zipfile.ZipFile(source) if kind == "archive" else None + try: + for asset_id, definition in video_assets.items(): + relative_path = definition["source"]["path"] + if archive is None: + video_path = source / relative_path + else: + video_path = root / f"{asset_id}{Path(relative_path).suffix}" + video_path.write_bytes(archive.read(relative_path)) + metadata = media_support.probe_video(video_path) + media_support.validate_host_video(metadata, codec=None) + results[asset_id] = {"path": relative_path, **metadata} + finally: + if archive is not None: + archive.close() + except ( + KeyError, + OSError, + RuntimeError, + ValueError, + subprocess.CalledProcessError, + zipfile.BadZipFile, + ) as error: + raise HostVerificationError(f"Appearance video validation failed: {error}") from error + return results + + +def verify_host(repo: Path, source: Path) -> dict[str, Any]: + web_ui = repo / "src" / "web-ui" + registry_module = web_ui / "src" / "infrastructure" / "appearance" / "registry" / "defaultAppearanceRegistry.ts" + if not registry_module.is_file(): + raise HostVerificationError(f"Not a BitFun checkout with the Appearance registry: {repo}") + + registry = registry_sync.export_registry(repo) + kind = appearance.source_kind(source) + standalone_warnings: list[dict[str, str]] = [] + manifest = ( + appearance.validate_project(source, registry, standalone_warnings) + if kind == "project" + else appearance.validate_archive(source, registry, standalone_warnings) + ) + video_assets = verify_video_assets(source, manifest) + node_source = r""" +import fs from 'node:fs'; +import { createServer } from 'vite'; +const manifestPath = process.argv[1]; +const server = await createServer({ root: process.cwd(), logLevel: 'silent', appType: 'custom', server: { middlewareMode: true } }); +try { + const registryModule = await server.ssrLoadModule('/src/infrastructure/appearance/registry/defaultAppearanceRegistry.ts'); + const validatorModule = await server.ssrLoadModule('/src/infrastructure/appearance/schema/AppearancePackageValidator.ts'); + const compilerModule = await server.ssrLoadModule('/src/infrastructure/appearance/compiler/AppearanceCompiler.ts'); + const input = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); + const registry = registryModule.createDefaultAppearanceRegistry(); + const validation = validatorModule.appearancePackageValidator.validate(input, registry); + const output = { + valid: validation.valid, + errors: validation.errors, + warnings: validation.warnings, + diagnostics: [], + cssBytes: 0, + }; + if (validation.valid) { + const snapshot = new compilerModule.AppearanceCompiler(registry).compile(input, 1); + output.diagnostics = snapshot.diagnostics; + output.cssBytes = Buffer.byteLength(snapshot.cssText, 'utf8'); + } + process.stdout.write(JSON.stringify(output)); +} finally { + await server.close(); +} +""" + with tempfile.TemporaryDirectory(prefix="bitfun-host-appearance-") as temporary: + manifest_path = Path(temporary) / appearance.MANIFEST_NAME + manifest_path.write_bytes(appearance.manifest_bytes(manifest)) + raw = run(["node", "--input-type=module", "--eval", node_source, str(manifest_path)], web_ui) + try: + result = json.loads(raw) + except json.JSONDecodeError as error: + raise HostVerificationError(f"BitFun host verifier produced invalid JSON: {error}\n{raw[:500]}") from error + + revision = run(["git", "rev-parse", "HEAD"], repo) + dirty = bool(run(["git", "status", "--porcelain=v1"], repo)) + issues: dict[tuple[str, str, str], dict[str, Any]] = {} + for item in [*standalone_warnings, *result.get("warnings", []), *result.get("diagnostics", [])]: + if not isinstance(item, dict): + continue + key = (str(item.get("path", "$")), str(item.get("code", "HOST_WARNING")), str(item.get("message", ""))) + issues[key] = item + return { + "schema": "bitfun.appearance.host-verification", + "schemaVersion": 1, + "appearanceId": manifest.get("id"), + "source": str(source), + "repo": str(repo), + "sourceRevision": revision, + "sourceDirty": dirty, + "valid": bool(result.get("valid")) and not result.get("errors"), + "errors": result.get("errors", []), + "warnings": list(issues.values()), + "cssBytes": result.get("cssBytes", 0), + "videoAssets": video_assets, + } + + +def create_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Validate and compile an Appearance with the real BitFun host") + parser.add_argument("repo", help="BitFun repository root") + parser.add_argument("source", help="Appearance project directory or .bitfun-appearance archive") + parser.add_argument("--report", help="write a JSON verification report") + parser.add_argument("--strict-warnings", action="store_true", help="fail verification when warnings are present") + parser.add_argument("--allow-warnings", action="store_true", help=argparse.SUPPRESS) + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + args = create_parser().parse_args(argv) + try: + result = verify_host(Path(args.repo).resolve(), Path(args.source).resolve()) + if args.report: + report_path = Path(args.report).resolve() + report_path.parent.mkdir(parents=True, exist_ok=True) + report_path.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(json.dumps(result, ensure_ascii=False, indent=2)) + passed = result["valid"] and (not args.strict_warnings or not result["warnings"]) + return 0 if passed else 1 + except (HostVerificationError, appearance.AppearanceError, registry_sync.SyncError, OSError, ValueError) as error: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/crates/assembly/core/src/agentic/tools/implementations/skills/builtin.rs b/src/crates/assembly/core/src/agentic/tools/implementations/skills/builtin.rs index 1363f90ee4..dbc9759b85 100644 --- a/src/crates/assembly/core/src/agentic/tools/implementations/skills/builtin.rs +++ b/src/crates/assembly/core/src/agentic/tools/implementations/skills/builtin.rs @@ -536,6 +536,21 @@ mod tests { ); } + #[test] + fn create_bitfun_skin_embeds_authoring_contract_and_example() { + let skill = embedded_skill_text("create-bitfun-skin/SKILL.md"); + assert!(skill.contains("name: create-bitfun-skin")); + + let registry = + embedded_skill_text("create-bitfun-skin/references/appearance-registry.json"); + assert!(registry.contains("schemaVersion")); + + let example = embedded_skill_text( + "create-bitfun-skin/examples/cinematic-animated-wallpaper/SKILL.md", + ); + assert!(example.contains("cinematic animated-wallpaper")); + } + #[test] fn office_helpers_use_validated_archive_extraction() { for skill in ["docx", "pptx", "xlsx"] { diff --git a/src/crates/assembly/core/src/agentic/tools/implementations/skills/catalog.rs b/src/crates/assembly/core/src/agentic/tools/implementations/skills/catalog.rs index aacea7b39a..009c0a4263 100644 --- a/src/crates/assembly/core/src/agentic/tools/implementations/skills/catalog.rs +++ b/src/crates/assembly/core/src/agentic/tools/implementations/skills/catalog.rs @@ -16,6 +16,7 @@ mod tests { assert_eq!(builtin_skill_group_key("ppt-design"), Some("office")); assert_eq!(builtin_skill_group_key("pptx"), Some("office")); assert_eq!(builtin_skill_group_key("xlsx"), Some("office")); + assert_eq!(builtin_skill_group_key("create-bitfun-skin"), Some("meta")); assert_eq!(builtin_skill_group_key("find-skills"), Some("meta")); assert_eq!(builtin_skill_group_key("miniapp-dev"), Some("miniapp")); assert_eq!(builtin_skill_group_key("writing-skills"), Some("meta")); diff --git a/src/crates/execution/agent-runtime/src/skills/catalog.rs b/src/crates/execution/agent-runtime/src/skills/catalog.rs index 79b769af87..10cefe85e1 100644 --- a/src/crates/execution/agent-runtime/src/skills/catalog.rs +++ b/src/crates/execution/agent-runtime/src/skills/catalog.rs @@ -44,6 +44,10 @@ pub(super) const BUILTIN_SKILL_SPECS: &[BuiltinSkillSpec] = &[ dir_name: "bitfun-canvas", group: BuiltinSkillGroup::Canvas, }, + BuiltinSkillSpec { + dir_name: "create-bitfun-skin", + group: BuiltinSkillGroup::Meta, + }, BuiltinSkillSpec { dir_name: "docx", group: BuiltinSkillGroup::Office, diff --git a/src/crates/execution/agent-runtime/tests/skill_contracts.rs b/src/crates/execution/agent-runtime/tests/skill_contracts.rs index a58234dded..3205b19a18 100644 --- a/src/crates/execution/agent-runtime/tests/skill_contracts.rs +++ b/src/crates/execution/agent-runtime/tests/skill_contracts.rs @@ -281,6 +281,7 @@ fn project_skill(dir_name: &str) -> SkillInfo { #[test] fn builtin_skill_catalog_and_mode_policy_are_runtime_owned() { assert_eq!(builtin_skill_group_key("docx"), Some("office")); + assert_eq!(builtin_skill_group_key("create-bitfun-skin"), Some("meta")); assert_eq!(builtin_skill_group_key("find-skills"), Some("meta")); assert_eq!(builtin_skill_group_key("miniapp-dev"), Some("miniapp")); assert_eq!( @@ -302,6 +303,10 @@ fn builtin_skill_catalog_and_mode_policy_are_runtime_owned() { resolve_builtin_default_enabled("ppt-design", "Cowork"), Some(true) ); + assert_eq!( + resolve_builtin_default_enabled("create-bitfun-skin", "DeepResearch"), + Some(true) + ); assert_eq!( resolve_builtin_default_enabled("find-skills", "DeepResearch"), Some(true)