Commit Graph
326 Commits
Author SHA1 Message Date
TomandGitHub 5a0f954297 fix(daemon): emit tool_use from tool_execution_start in pi-rpc (#186)
* fix(daemon): emit tool_use from tool_execution_start in pi-rpc

The pi-rpc adapter emitted tool_use from message_end, which fires
before tool execution starts. The web UI pairs tool results to prior
tool_use events, so receiving tool_result without a preceding tool_use
broke tool card rendering and file auto-open behavior.

Move tool_use emission to tool_execution_start, matching the pattern
in copilot-stream.ts. Remove redundant tool call extraction from
message_end (tool_use is now emitted at execution time, usage is
already emitted from turn_end).

Extract mapPiRpcEvent as a pure exported function so tests exercise
the real event mapping logic instead of an inlined copy that can
diverge from production.

Ref: mrcfps review comments on PR #117

* docs(daemon): clarify mapPiRpcEvent mutability contract

The function mutates ctx.sentFirstToken to track streaming state.
Calling it "pure" is misleading; revised the doc comment to say
no I/O or child process interaction instead.

* fix(pi-rpc): remove redundant status(tool) emission from tool_execution_start

Now that tool_use fires inline from tool_execution_start, the
accompanying status(tool) event is redundant: tool_use already
carries the tool name, and the UI renders running state from the
tool card. The extra status pill breaks consecutive tool_use
grouping in AssistantMessage.buildBlocks. Aligns with
copilot-stream, which emits only tool_use from
tool.execution_start with no status event.
2026-05-02 16:06:37 +08:00
SidandGitHub d58b07fdad feat(daemon): auto-detect OpenClaude as a fallback for Claude Code (#263)
Today open-design detects code agents by exact PATH binary name (`claude`,
`codex`, `gemini`, …). OpenClaude (https://github.com/Gitlawb/openclaude)
is an argv-compatible Claude Code fork that ships under the binary name
`openclaude`, so users with only `openclaude` installed see "Claude Code:
not detected" and have to write a wrapper script (issue #235, "Path B"
workaround).

This change adds a generic fallback-bin mechanism and uses it to declare
`openclaude` as a Claude Code fallback:

- `AGENT_DEFS[claude]` gains `fallbackBins: ['openclaude']` — a new
  optional declarative slot. Agents without a fallback are unchanged.
- `resolveAgentExecutable(def)` is a new exported helper that walks
  `[def.bin, ...def.fallbackBins]` and returns the first PATH hit, or
  null. The two existing `resolveOnPath(def.bin)` callsites in
  `probe()` and `resolveAgentBin()` route through this helper instead
  of reading `def.bin` directly.
- `stripFns()` is extended to drop `fallbackBins` from the API response
  payload — same posture as `helpArgs` / `capabilityFlags` / probe-only
  metadata that shouldn't bleed into the public agents list.

Behavior contract:

- If both `claude` and `openclaude` are on PATH, `claude` still wins
  (the order in the candidate list matters).
- If only `openclaude` is on PATH, the agent shows up as "Claude Code"
  and chat / model-listing / spawning all use the resolved openclaude
  path. Users don't need wrappers and the agent picker stays a single
  "Claude Code" entry instead of confusingly splitting into two.
- Agents with no `fallbackBins` field (codex / gemini / opencode / …)
  resolve via `def.bin` exactly as before.

Why fallback (not a separate AGENT_DEFS entry):

The issue author explicitly preferred the fallback shape over a second
top-level "OpenClaude" entry, and OpenClaude is a verbatim Claude Code
fork — the buildArgs / fallbackModels / streamFormat / capabilityFlags
would all be duplicated. A new `fallbackBins` slot keeps the Claude
config single-sourced and lets future drop-in forks be added with one
extra string.

Test coverage (5 new tests in `tests/agents.test.ts`):

1. `claude.fallbackBins` includes `'openclaude'` — declarative regression
   guard so the AGENT_DEFS entry can't silently drift back.
2. `resolveAgentExecutable` prefers `def.bin` when both candidates are
   present (primary always wins).
3. `resolveAgentExecutable` falls back through `fallbackBins` when
   `def.bin` is missing (the OpenClaude-only install case).
4. `resolveAgentExecutable` returns null when no candidate is on PATH.
5. `resolveAgentExecutable` handles defs with no `fallbackBins` field
   (no-regression guard for codex / gemini / opencode / …).

The 4 filesystem-backed cases skip on Windows because resolveOnPath
walks PATHEXT extensions there and the fixture files don't carry them;
the declarative AGENT_DEFS assertion runs on every platform.

All 230 daemon tests pass locally (was 225 before this PR).

Closes #235
2026-05-02 15:29:07 +08:00
SidandGitHub b9d8bd36b9 fix(daemon): treat .py files as previewable code in Design Files (#261)
Python files in Design Files cannot be previewed inline today: the side
panel falls back to BinaryViewer and users can only download or open
externally, even though `.py` is plain text and reasonable to inspect
in-app.

Root cause: `kindFor()` in `apps/daemon/src/projects.ts` (line 278)
classifies files into coarse buckets the frontend uses to pick a viewer.
The `'code'` branch lists `.js / .mjs / .cjs / .ts / .tsx / .json / .css`
but not `.py`, so Python files fall through to `'binary'`. The frontend
already renders the `'code'` bucket via `TextViewer` in
`apps/web/src/components/FileViewer.tsx`, so once a file lands in the
`'code'` bucket it gets the existing read-only code preview for free.

Fix: add `'.py'` to the `'code'` extension list. No frontend change
needed — `TextViewer` handles it on receipt.

Scope: this PR keeps the change minimal to match the issue
(`.py` only). Other commonly-requested text-like extensions
(`.yaml / .yml / .toml / .sh`, plus other code languages) can be added
in a follow-up if maintainers want that surface area.

Closes #61
2026-05-02 15:06:24 +08:00
SidandGitHub bb11c68b9f fix(i18n): replace hardcoded 'Claude' with '助手' in zh-TW assistant role (#262)
Spotted by @lefarcen in #59 follow-up review: while every other locale
file has been switched to a neutral, localized assistant label
(`Assistant` / `助手` / `アシスタント` / `Assistent` / `Assistente` / etc.),
`zh-TW.ts` still hardcodes `'Claude'` for `'assistant.role'`. That string
is the per-message fallback used by `assistantRoleLabel()` in
`apps/web/src/components/AssistantMessage.tsx` when no agent metadata is
attached to a turn — so on Traditional Chinese installs running Codex /
Cursor / Gemini / OpenCode, the older message rows can still render as
"Claude" instead of a neutral label.

Fix: align zh-TW with zh-CN by setting `'assistant.role': '助手'`. The
character `助手` is identical in Simplified and Traditional Chinese, so
this matches the rest of the locale file's register without introducing
any character-form drift.

Refs #59
2026-05-02 14:59:33 +08:00
RinandGitHub 06b1492744 Standardize agent communication via stdin and remove Windows-specific shims logic (#258)
This change moves Claude Code and GitHub Copilot CLI to stdin-based prompt delivery, aligning them with other agents. By doing so, we can bypass OS command-line length limits on Windows and set shell: false universally, improving security and robustness.

Integration of createCommandInvocation ensures that Windows npm shims (.cmd/.bat) are correctly handled while keeping shell: false, addressing the P1 warning from the automated review and maintaining consistent execution across platforms.
2026-05-02 14:59:21 +08:00
SidandGitHub 52677555f7 fix(daemon): include package.json in tarball so packaged app reports correct version (#260)
The packaged app's Settings → About page displays version `0.0.0` instead
of the actual release version because the daemon's `package.json` was
excluded from its `pnpm pack` tarball.

Root cause: `apps/daemon/package.json` declares `"files": ["dist"]`, so
`pnpm pack` only includes the `dist/` directory. At runtime,
`readCurrentAppVersionInfo()` in `apps/daemon/src/app-version.ts` resolves
`new URL('../package.json', import.meta.url)` from the compiled
`dist/app-version.js`, which points at the tarball's root `package.json`.
Because that file isn't packed, the read fails silently, falls through
the catch in `readPackageMetadata()`, and the version falls back to the
`APP_VERSION_FALLBACK = '0.0.0'` constant. `/api/version` and
`/api/health` then report `'0.0.0'` for every packaged install.

Fix: add `"package.json"` to the daemon's `files` array so it ships in
the tarball. The package already declares `"./package.json"` as an
exports entry, so consumers expect this file to be available.

Closes #224
2026-05-02 14:52:23 +08:00
Marc ChanandGitHub 0e166bb799 Optimize Electron release artifact size (#249) 2026-05-02 14:22:16 +08:00
lefarcenandGitHub c2b3d737f2 fix: make max_tokens configurable (closes #29) (#78)
* fix(web,daemon): make max_tokens configurable (closes #29)

BYOK users on custom Anthropic-compatible providers (e.g. Xiaomi MiMo)
hit the hardcoded 8192 cap and saw artifacts truncated mid-stream.

- AppConfig.maxTokens with Settings input (EN/CN + 8 other locales)
- ProxyStreamRequest.maxTokens contract field
- anthropic, anthropic-compatible, and openai-compatible providers all
  forward cfg.maxTokens
- /api/proxy/anthropic/stream and /api/proxy/stream payloads honor it,
  defaulting to 8192 when unset so prior clients are unaffected

Original sketch by @mashu in #78 (50a9d14); rebased to the apps/web
layout and extended to the proxy paths actually used when baseUrl is
set, which is where #29's user actually traffics.

* feat(web): per-model max_tokens defaults

Adds a hand-maintained MODEL_MAX_TOKENS table (Claude 4.5 line → 64k,
mimo-v2.5-pro → 32k) and an effectiveMaxTokens helper layered over the
override field added in 6a3ae5f, so #29's user — and others on supported
models — don't have to discover Settings to avoid mid-stream truncation.

- apps/web/src/state/maxTokens.ts: lookup + helpers
- providers/{anthropic,anthropic-compatible,openai-compatible}.ts:
  forward effectiveMaxTokens(cfg) instead of cfg.maxTokens ?? 8192
- SettingsDialog: input becomes an optional override (blank = default,
  shown as placeholder)
- 10 locale hint strings updated to the new semantics

* feat(web): vendor LiteLLM model metadata for max_tokens defaults

Replaces the 4-entry hand-rolled MODEL_MAX_TOKENS map from 544e67e with
a vendored slice of BerriAI/litellm's model_prices_and_context_window
JSON (1970 chat models, ~97KB raw / ~25KB gzip). Future model launches
land in maxTokens.ts via `pnpm sync-litellm-models` instead of manual
edits.

- scripts/sync-litellm-models.ts: fetches the upstream JSON, filters to
  chat-mode entries, projects each entry to its max_output_tokens (or
  max_tokens fallback), and writes a sorted, license-attributed JSON
- apps/web/src/state/litellm-models.json: generated artifact, committed
- apps/web/src/state/maxTokens.ts: lookup is now
  OVERRIDES → LITELLM_MODELS → FALLBACK_MAX_TOKENS. The OVERRIDES table
  shrinks to just `mimo-v2.5-pro` (LiteLLM only ships MiMo via
  OpenRouter/Novita aliases, not the canonical id Xiaomi's API uses).

LiteLLM is MIT-licensed (BerriAI/litellm/blob/main/LICENSE); attribution
is preserved in both the script header and the generated JSON's
_license field.

* test(web,docs): cover maxTokens lookup + document sync workflow

- apps/web/src/state/maxTokens.test.ts: six vitest cases pinning the
  three-tier lookup (override → LiteLLM → fallback) and the
  effectiveMaxTokens user-override path. Guards against a future sync
  silently dropping the Anthropic 4.5 entries we rely on.
- CONTRIBUTING.md / CONTRIBUTING.zh-CN.md: new "Updating model
  max_tokens metadata" section pointing future maintainers at
  scripts/sync-litellm-models.ts and explaining when OVERRIDES is
  appropriate (it's the rare exception, not the default).

* fix(web): mark Max tokens label as optional in 10 locales

The Settings field is optional (blank means "use the per-model default")
but the label gave no visual cue, breaking the implicit pattern that
every other API-mode field (key/model/baseUrl) is required. Append
"(optional)" — using the locale's natural parenthetical convention
(Chinese full-width brackets, Japanese 任意, Russian опционально, etc.)
— so the field reads as discretionary at a glance.

* fix(web): validate maxTokens override against advertised UI bounds

Addresses Siri-Ray's review on commit 0d98185. The Settings input
declares min={1024}/max={200000}/step={1024}, but until now
effectiveMaxTokens trusted any defined cfg.maxTokens, so a stale or
hand-edited localStorage value (negative, zero, fractional, billions)
would pass straight to the Anthropic SDK on the direct path while the
daemon proxy quietly clamped it back to 8192 on the proxied path —
same config, divergent behavior depending on route.

- maxTokens.ts: add MIN_MAX_TOKENS / MAX_MAX_TOKENS exports and
  isValidOverride helper. effectiveMaxTokens only honors the override
  when it is a finite integer in [1024, 200000]; otherwise falls back
  to modelMaxTokensDefault.
- SettingsDialog.tsx: input bounds now reference the same constants so
  the UI promise can't drift from the runtime check.
- maxTokens.test.ts: six new cases pinning the rejection of negative,
  zero, sub-MIN, super-MAX, non-integer (fractional / NaN / Infinity)
  overrides plus the inclusive MIN/MAX boundaries.

The daemon proxy's existing `> 0` fallback stays as defense-in-depth.
2026-05-02 13:52:54 +08:00
lefarcenandGitHub 27ef18b7c0 fix(web): suppress hydration warning on <body> (#248)
Browser extensions (e.g. YouMind, Grammarly, ColorZilla) inject
attributes onto <body> before React hydrates, producing a
"server rendered HTML didn't match client" console error in dev.

`<html>` already had suppressHydrationWarning; mirror it on `<body>`
so any extension that targets the body element no longer surfaces
a false-positive hydration mismatch to end users.

Closes #243
2026-05-02 13:18:02 +08:00
YoungandGitHub ee753fa365 feat(i18n): add Korean (ko) localization (#253) 2026-05-02 12:56:22 +08:00
Jie ZhuandGitHub f863c484a4 fix(web): add scroll to settings language menu when it overflows view… (#247)
* fix(web): add scroll to settings language menu when it overflows viewport

The language picker dropdown in the settings dialog opens upward but had
no max-height constraint, causing top options to be clipped off-screen.
Add max-height with overflow-y: auto and switch from bottom to top +
transform positioning so the scrollbar actually activates.

* docs(web): add comment explaining language menu max-height magic numbers

* fix(web): constrain language menu height to available space above trigger

The previous translateY(-100%) approach clipped the menu above the
viewport when the trigger was near the top edge. Switch to bottom
positioning with a JS-computed CSS variable (--menu-available-h) so
the menu height never exceeds the distance to the viewport top.
2026-05-02 12:44:23 +08:00
Siri-RayandGitHub 3d306cb450 Add React artifact output support (#121)
* Add React artifact output support

Generated-By: looper 0.2.7 (runner=worker, agent=codex)

* Fix React component artifact preview repairs

Generated-By: looper 0.2.7 (runner=fixer, agent=codex)

* fix: address React preview review feedback

Generated-By: looper 0.2.7 (runner=fixer, agent=codex)
2026-05-02 11:15:18 +08:00
Siri-RayandGitHub 0bafc73d24 Add visible conversation timestamps (#120)
* Add visible conversation timestamps

Generated-By: looper 0.2.7 (runner=worker, agent=codex)

* Fix assistant message timestamps

Generated-By: looper 0.2.7 (runner=fixer, agent=codex)
2026-05-02 11:15:07 +08:00
Siri-RayandGitHub 85032f530c fix(web): preserve deck preview pagination per file (#119)
Generated-By: looper 0.2.7 (runner=worker, agent=codex)
2026-05-02 11:13:49 +08:00
Siri-RayandGitHub 4e3d82cdcd Fix deck preview pagination controls (#112)
* fix: correct deck preview pagination controls

Generated-By: looper 0.2.7 (runner=worker, agent=codex)

* fix: correct deck preview pagination controls

Generated-By: looper 0.2.7 (runner=fixer, agent=codex)
2026-05-02 11:02:47 +08:00
Tom HuangandGitHub 266daf904f feat(prompt-templates): add 11 HyperFrames video prompts + media generation README section (#227)
* feat(prompt-templates): add 11 HyperFrames video prompts and surface media generation in README

Adds eleven `hyperframes-*` prompt templates under `prompt-templates/video/`,
each one a concrete brief with scene-by-scene timing, GSAP eases, palette,
and the HyperFrames non-negotiables (deterministic, paused timelines,
entrance-only motion, lint/inspect commands). Archetypes covered:

- minimal product reveal (5s, 16:9)
- SaaS product promo (30s, 16:9, Linear/ClickUp-style)
- TikTok karaoke talking-head (9:16, TTS + word-synced captions)
- brand sizzle reel (30s, beat-synced kinetic typography)
- animated bar-chart race (NYT-style data infographic)
- Apple-style flight map route (origin → destination)
- 4s cinematic logo outro
- $0 → $10K money counter hype (9:16)
- 3-phone app showcase
- 9:16 social overlay stack (X · Reddit · Spotify · Instagram)
- 15s website-to-video pipeline

Each template uses `model: "hyperframes-html"`, real catalog-block thumbnails
from HeyGen's CDN as previewImageUrl, and source attribution to
`heygen-com/hyperframes` (Apache-2.0).

README also gets a new **Media generation** section between *Visual directions*
and *Beyond chat*, plus a new row in the *At a glance* table. The section
documents the three model families currently surfaced as templates
(gpt-image-2, Seedance 2.0, HyperFrames) with example galleries — gpt-image-2
thumbnails, Seedance MP4-linked thumbnails, and the 11 HyperFrames tiles —
and notes the wider model coverage (Kling, Veo, Sora, MiniMax, Suno, Udio,
Lyria, TTS) already wired in `VIDEO_MODELS` / `AUDIO_MODELS_BY_KIND` and
open for community templates.

* i18n(de): register new HyperFrames templates, categories, tags

Adds German titles/summaries for the 11 new hyperframes-* video templates
plus the Product/Marketing/Data/Travel/Branding/Short Form categories and
hyperframes/title-card/sizzle/etc. tags they introduce, so the German sync
guarantees enforced by apps/web/src/i18n/content.test.ts hold.

* docs(readme): sync Media generation section to de / ja / ko / zh-CN; bump counts to 93 (43 + 39 + 11)

Mirrors the English Media generation row + section into the four locale READMEs
(README.de.md, README.ja-JP.md, README.ko.md, README.zh-CN.md), translating
prose / table headers / captions while keeping the gpt-image-2, Seedance MP4,
and HyperFrames catalog-block thumbnails identical across all five locales so
the galleries render the same images.

Counts updated to reflect current main (after rebase): 43 gpt-image-2 + 39
Seedance + 11 HyperFrames = 93 prompts total. The English README's At-a-glance
row, intro paragraph, and gallery sub-headers now read "sample of 43" /
"sample of 39" / "11 ready-to-replicate templates" — locales follow.

Resolves the Codex review's German-i18n flag end-to-end: README copy is in
sync, and the German content map (DE_PROMPT_TEMPLATE_*) was already extended
in the prior commit on this branch.
2026-05-02 11:01:16 +08:00
Tom HuangandGitHub 56aa985b1a feat(design-systems): integrate kami as editorial paper system + deck starter (#226)
* feat(design-systems): integrate kami as editorial paper system + deck starter

- Add design-systems/kami/DESIGN.md adapting kami's tokens, ten invariants,
  and type/color/component rules into the OD DESIGN.md spec. Lands under a
  new "Editorial & Print" category in the picker.
- Add templates/kami-deck.html: a kami-flavored variant of deck-framework.html
  (parchment canvas, ink-blue accent, single-weight serif) with five demo
  slides — cover, agenda, metric row, two-column body+pull-quote, closing —
  so it doubles as a worked example for slide and prototype work.
- Update design-systems/README.md and README.md to list and credit kami
  (MIT, tw93/kami).
- Update apps/web/src/i18n/content.ts so the i18n coverage test passes:
  add the German summary for 'kami' and the 'Editorial & Print' category.

* docs(design-systems/kami): address PR #226 review notes

Five P3 polish edits from @lefarcen's review (LGTM, non-blocking):

1. Add a "When to swap the stack" subsection in §3 spelling out how the
   three CJK font stacks combine: set the dominant-language stack on
   :root, scope per-section overrides for mixed-language artifacts,
   never chain all three families inside one font-family declaration.

2. Reframe the brand as "kami / 紙 / 纸" so the system reads as
   co-designed across EN, zh-CN, and ja from the start, not Japan-centric
   with i18n bolted on. Title and §1 lede updated.

3. Reconcile the pt → px ratios into one table at the top of §3
   "Hierarchy": print pt × ~1.33 for page artifacts, × 1.6 for slide
   macro tokens, × 0.6 for slide micro tokens. Drop the duplicate
   ratio bullets from §5 "Slides".

4. Keep the soft tag-brush gradient exception but make it real: add
   the .tag.brush CSS + an inline <span class="tag brush"> example
   in §4, and surface it once on slide 04 of the kami deck so agents
   see exactly when the carve-out applies.

5. Add a "Tabular-nums contexts" subsection in §3 enumerating every
   place numbers should opt into tabular-nums (metrics, footers,
   section numbers, dates, financial tables, KPI grids, version
   numbers, side-by-side comparisons) — and the rule for when not to
   (single numbers in running prose).
2026-05-02 11:00:56 +08:00
Tom HuangandGitHub f4ab52d9dd feat(skills): integrate lewislulu/html-ppt-skill + 15 per-template Examples cards (#193)
* feat(skills): integrate lewislulu/html-ppt-skill as html-ppt + 15 per-template Examples cards

Bring the MIT-licensed lewislulu/html-ppt-skill upstream into skills/html-ppt/
with its full asset tree (36 themes, 31 single-page layouts, 27 CSS + 20
canvas-FX animations, runtime + presenter mode, all 15 full-deck templates,
and the upstream LICENSE preserved verbatim).

Surface each full-deck template as its own Examples gallery card via thin
wrapper skills under skills/html-ppt-<template>/. Each wrapper ships:

- SKILL.md with `od.mode=deck`, scenario, `featured: 20-34` (slotting after
  the existing curated cards), an `od.example_prompt` tuned to the template,
  and `od.upstream` pointing at the upstream repo. Clicking "Use this prompt"
  on a card now wires up `kind=deck` + `speakerNotes=true` and seeds the
  composer with the upstream's authoring flow so the prompt -> output path
  matches the upstream demo.
- example.html baked self-contained (fonts/base/animations/style/theme CSS
  inlined, runtime <script> stripped) so the gallery srcdoc iframe renders
  the upstream look without external paths.

scripts/scaffold-html-ppt-skills.mjs and scripts/bake-html-ppt-examples.mjs
are idempotent generators — re-run after editing skills/html-ppt/ to re-sync
all per-template wrappers and their baked examples.

Add a Credits section + extend the License section in README.md /
README.zh-CN.md / README.ko.md to credit the upstream alongside the
already-cited op7418/guizang-ppt-skill.

* fix(scripts): allowlist html-ppt skill JS for residual-js check

Add scripts/bake-html-ppt-examples.mjs and scripts/scaffold-html-ppt-skills.mjs
to allowedExactPaths, and skills/html-ppt/assets/ to allowedPathPrefixes so
pnpm check:residual-js no longer flags the vendored upstream runtime JS or the
new maintainer-only .mjs scripts.

* fix(skills): keep all slides in baked html-ppt examples + correct asset guidance

The bake script's `STATIC_FALLBACK_CSS` set `.slide+.slide{display:none}`,
which silently truncated every baked `example.html` to slide 1. That artifact
is also served by `/api/skills/:id/example` and reused by the Examples
preview modal's share/export and print-to-PDF, so the rule dropped the rest
of the deck from those flows. Drop the rule — slides now stack in the
print-style flow the surrounding comment already described, the gallery
thumbnail iframe still naturally lands on slide 1 (each `.slide` is `100vh`),
and modal/share/export contains the full deck.

The wrapper SKILL.md authoring instructions told agents to copy
`index.html` + `style.css` into a project while keeping the upstream
`../../../assets/...` links, but those parent-relative URLs only resolve
in-tree (the template sits three folders deep). Once the file lives in a
project artifact, `base.css`, `animations.css`, and `runtime.js` 404 and
the deck never activates. Replace step 3 with two recipes — copy the
shared assets into a project-local `assets/` and rewrite the four tags,
or inline the CSS/JS directly — and re-emit all 15 wrapper SKILL.md
files via the scaffold generator.
2026-05-02 11:00:44 +08:00
1edab990bb feat(craft): add brand-agnostic craft references + Refero-derived lint rules (#225)
* feat(craft): add brand-agnostic craft references and refero-derived lint rules

Introduce `craft/` as a third top-level content axis alongside `skills/`
and `design-systems/`, holding universal (brand-agnostic) craft rules
that apply on top of any DESIGN.md. Skills opt in via a new
`od.craft.requires` front-matter array; the daemon resolves the slug
list and injects the matching files between DESIGN.md and the skill
body in the system prompt.

Initial vendor (MIT, adapted from referodesign/refero_skill): typography
craft, color craft, anti-ai-slop. Pilot wired on saas-landing.

Extend the existing lint-artifact pass with two refero-derived rules:
- P0 ai-default-indigo — solid #6366f1 / #4f46e5 / #4338ca / #8b5cf6 as
  accent (not just gradients) is the most-reported AI tell.
- P1 all-caps-no-tracking — `text-transform: uppercase` rules without
  ≥0.06em letter-spacing.

The craft loader silently drops missing files so a skill can
forward-reference future sections (e.g. `motion`) without breaking.

* fix(daemon): skip :root token blocks in ai-default-indigo lint

The ai-default-indigo P0 check scanned the whole HTML for the raw
hex, so brands that intentionally encode indigo as `--accent: #6366f1`
in :root and consume it via var(--accent) downstream were flagged
as AI-default — a false positive that forced the agent to "fix"
valid output. Strip :root token-definition blocks (including
attribute-selector theme variants) before scanning, mirroring the
existing pattern used by the raw-hex P1 check. Hex still flagged
when it appears in component rules or inline styles.

* docs(craft): address PR #225 P3 review feedback

- craft/README.md: explain why missing craft sections are silently
  dropped (forward-compatibility) instead of surfacing a warning.
- craft/typography.md: ground the 0.06em ALL CAPS tracking floor in
  Bringhurst-derived typographic practice rather than presenting
  the threshold as unattributed.
- craft/color.md: cover the edge case where a brand's DESIGN.md
  intentionally encodes indigo as --accent — `var(--accent)` uses
  remain unflagged because the linter only inspects hardcoded hex.
- docs/skills-protocol.md: link the "missing files dropped silently"
  note back to craft/README.md for the canonical slug list and the
  rationale behind the choice.

* fix(craft): address PR #225 P0 review feedback

- tools/pack: copy `craft/` into the packaged resource root alongside
  `skills`, `design-systems`, and `frames`, so the `od.craft.requires`
  integration isn't a silent no-op when the daemon resolves
  `${OD_RESOURCE_ROOT}/craft` in packaged builds.
- packages/contracts: add `craftRequires?: string[]` to `SkillSummary`
  (and therefore `SkillDetail`) so the field that `listSkills()`
  already returns and `/api/skills(/:id)` already serializes via
  `...rest` is part of the documented web/daemon contract instead of
  leaking through as an untyped property.
- apps/daemon/lint-artifact: expand the indigo token-strip pass to
  cover selector lists containing `:root` (e.g. `:root, [data-theme="light"]`)
  and any rule whose body is custom-property-only (e.g. a
  `[data-theme="dark"] { --accent: ... }` theme variant). Real
  component rules with a hardcoded indigo are still preserved so the
  P0 finding still fires; tests cover the new selector-list and
  theme-variant cases.

* fix(craft): address PR #225 follow-up review feedback

- lint-artifact: scope the indigo token-strip to <style> blocks so the
  rule-shaped regex no longer captures leading `<style>` text into the
  selector (which broke `:root` recognition for token blocks that mix
  `color-scheme`/etc. with `--accent`). Run the strip on the extracted
  CSS instead, with a regression covering `:root { color-scheme: light;
  --accent: #6366f1 }`.
- lint-artifact: tighten the custom-property-only exemption to global
  theme-scope selectors (`:root`, `html`, `body`, bare attribute
  selectors like `[data-theme="dark"]`). Component-local rules such as
  `.cta { --cta-bg: #6366f1 }` are no longer exempted, so an agent
  cannot launder default indigo through a local var. Regression test
  added.
- craft/anti-ai-slop.md: stop claiming every rule below is enforced by
  the linter; only several are. The unenforced rules (standard
  Hero→Features→Pricing→FAQ→CTA flow, decorative blob/wave SVG
  backgrounds, perfect symmetry) are now flagged inline as
  "(guidance, not auto-checked)" so the contract with the lint surface
  stays honest.

* fix(daemon): tighten lint-artifact iteration and :root token gating

- all-caps-no-tracking: iterate every <style> block. The previous
  check called `exec` once on a non-global regex, so an artifact
  whose offending uppercase rule sat in a second <style> block
  (e.g. a reset block followed by a components block) slipped
  past. Switch to `matchAll` and break across both loops once a
  violation is found. Regression test covers a second-block
  uppercase rule.
- ai-default-indigo: stop unconditionally exempting any selector
  list containing `:root`. The exemption now requires both
  conditions to hold: every selector in the list is global theme
  scope AND the body is token-shaped (CSS custom properties or
  the `color-scheme` keyword). So `:root { background: #6366f1 }`
  and `:root, .cta { --cta-bg: #6366f1 }` no longer launder a
  hardcoded indigo through the strip pass. Regression tests cover
  both bypass shapes.

* fix(daemon): scope theme-attr exemption and strip CSS comments in token blocks

Address PR #225 review feedback on `ai-default-indigo`:

- The bare-attribute branch of `selectorListIsGlobalThemeScope` accepted
  any `[attr=...]` selector, so a custom-property-only rule on a
  component/state attribute (e.g. `[data-variant="primary"]`,
  `[aria-current="page"]`) was treated as a global theme block and
  stripped before the indigo scan — exactly the component-local indigo
  laundering this lint is meant to catch. Restrict the exemption to a
  small allowlist of known theme switches: `data-theme`,
  `data-color-scheme`, `data-mode`.
- `stripTokenBlocksFromCss` split rule bodies on `;` and matched each
  fragment from the start, so a token block whose body contained a
  normal CSS comment such as `:root { /* brand accent */ --accent:
  #6366f1; }` produced a fragment beginning with the comment, failed
  `isTokenShapedDeclaration`, and the rule was left in scope of the
  indigo scan — a false P0 on a legitimate token definition. Strip CSS
  comments before splitting/classifying declarations.

Add regression coverage: arbitrary component/state attribute selectors
still trip `ai-default-indigo`; `data-color-scheme` theme variants stay
exempted; `:root` token blocks with leading, trailing, and
between-declaration CSS comments are recognized.

* fix(daemon): strip CSS comments and recognize tokens nested in at-rules

The all-caps-no-tracking scan ran against raw `<style>` content, so a
commented-out rule like `/* .eyebrow { text-transform: uppercase; } */`
matched `upperRe` and emitted a P1 for CSS the browser ignores. Strip
CSS comments from the style body before structural matching.

`stripTokenBlocksFromCss` only matched flat `selector { body }` rules,
so a media-query-wrapped token block like
`@media (prefers-color-scheme: dark) { :root { --accent: #6366f1 } }`
had its outer `@media` rule treated as the selector/body pair and the
inner `:root` token block was never stripped, producing a P0 false
positive on legitimate responsive theme CSS. Tighten the body
alternation to `[^{}]*` so the regex matches innermost rules and
recognizes the inner `:root` block directly while preserving the
outer at-rule wrapper.

* fix(daemon): align ai-default-indigo list with documented cardinal sins

The lint's AI_DEFAULT_INDIGO subset omitted #3730a3 and #a855f7, which
craft/anti-ai-slop.md lists as P0-blocked solid accents. An artifact
could hard-code one of those documented colors as a button fill and
slip past the indigo scan unless it happened to be inside a gradient.

Bring the lint set to the exact list documented in the craft doc, and
tighten the doc's wording from "etc." to an explicit enumeration that
points at AI_DEFAULT_INDIGO so the prompt contract and daemon behavior
stay in sync. Add regression tests pinning each newly-included hex.

* fix(daemon): tighten theme-scope selector and scan inline ALL CAPS

The theme-scope exemption used to accept any attribute on `:root`,
`html`, or `body` (e.g. `:root[data-variant="primary"]`), letting an
agent launder default indigo through a component/state attribute and
slip past the `ai-default-indigo` lint. The prefixed branches now
require the attribute name to be one of GLOBAL_THEME_ATTRIBUTES,
matching the bare-attribute branch.

The `all-caps-no-tracking` rule only iterated `<style>` blocks, so
inline declarations like `<span style="text-transform: uppercase">`
produced no finding even though craft/typography.md treats the
≥0.06em tracking floor as having no exceptions. Added a second scan
over `style="..."` attributes that runs the same letter-spacing
check and dedupes against the existing `<style>`-block finding so
the agent gets a single corrective signal per artifact.

* fix(daemon): align uppercase tracking px floor with the 0.06em rule

The previous absolute fallback (>=1.5px) was stricter than the craft
rule it enforces. `font-size: 12px; letter-spacing: 1px` is 0.083em
— above the 0.06em floor — but 1.5px would reject it and trigger an
unnecessary correction loop on compliant small-label CSS.

Extract `hasAdequateUppercaseTracking`: read `font-size` from the same
rule body and compare px tracking against `fontSize * 0.06`; fall back
to a conservative >=1px floor when font-size is inherited (covers the
default 16px body where 1px ≈ 0.0625em). Apply the helper to both the
<style>-block scan and the inline-style scan, and add 12–14px label
tests in both branches.

* fix(daemon): treat rem letter-spacing as absolute, not per-element em

`rem` was previously folded into the same branch as `em` and accepted
at the 0.06 threshold. But `rem` is relative to the root font-size
(16px default), not the element's own font-size, so on a 48px heading
`letter-spacing: 0.06rem` resolves to 0.96px — about 0.02em of the
element, well below the 0.06em rule the lint enforces.

Convert rem to absolute px through the 16px root assumption and reuse
the same px-vs-element-font-size resolution: same-rule `font-size: <n>px`
gives an exact `n * 0.06` floor; otherwise the conservative >=1px
fallback applies. Add regression tests for 48px headings with 0.06rem
tracking (must flag) plus the 16px-element and rem-floor matches that
must keep passing, in both <style>-block and inline-style branches.

* fix(daemon): resolve var() refs in uppercase tracking lint

`hasAdequateUppercaseTracking` only matched literal numeric values,
so a tokenized rule like `letter-spacing: var(--caps-tracking)` —
exactly the pattern the craft prompt steers artifacts toward — was
falsely reported as `all-caps-no-tracking`. Extract `--name: value`
declarations from global theme scopes (`:root`, `html`, theme-attribute
selectors) once per artifact, then expand simple `var(--name)` (and
`var(--name, fallback)`) references in the inspected rule body before
applying the existing 0.06em / px-floor / rem-conversion logic.
References without a matching token and no fallback stay in place,
preserving the conservative "missing tracking" finding.

* fix(daemon): resolve rem and var() font-size in uppercase tracking lint

Previously the px-vs-element-font-size resolution only matched
`font-size: <n>px`. Any rem-based or tokenized display size fell
through to the lenient `>= 1px` body-text fallback, so an artifact
emitting `.display { font-size: 3rem; text-transform: uppercase;
letter-spacing: 1px; }` (a ~48px heading with a 2.88px floor) slipped
past the lint that this helper exists to enforce.

Resolve `rem` font-size via the same root-font assumption already used
for tracking, and treat any explicitly declared but unresolvable unit
(`em`, `%`, `calc(...)`, an unresolved `var(...)`) conservatively —
refuse the lenient fallback so the rule must use either an `em`
letter-spacing or a verifiable px/rem font-size.

`var()` font-size declarations resolve through the existing
`resolveCssVars` pass before the size scan runs, so the same fix
catches the tokenized-display-size pattern (`--display-size: 3rem`).

* fix(daemon): parse declarations to ignore custom-prop names in uppercase tracking lint

The hasAdequateUppercaseTracking and resolveFontSizePx helpers used substring regexes against the rule body, so a token-name declaration such as `--letter-spacing: 0.08em` or `--display-font-size: 48px` could satisfy the `letter-spacing` / `font-size` checks even though it has no rendered effect — letting actual ALL-CAPS-without-tracking rules slip past the P1 lint.

Parse the declaration list, compare exact property names, and skip declarations whose property starts with `--`. Adds regression tests covering token-name letter-spacing (style-block + inline) and a token-name font-size masking the bail-out branch.

* fix(daemon): scope indigo token exemption to --accent only

Previously stripTokenBlocksFromCss removed every custom-property-only
global theme block before the ai-default-indigo scan, which let a
laundered indigo token like `:root { --primary: #6366f1 }` consumed
via `var(--primary)` slip past the lint. The craft contract is that
the only escape hatch is encoding indigo as the design system's
`--accent` token; any other token name is still the LLM-default
color hidden behind an arbitrary name. Narrow the strip pass so a
non-`--accent` token whose value carries an AI-default indigo hex
keeps the rule in scope, and add regression tests for `--primary` /
`--button-bg` global tokens feeding a CTA, including the at-rule
and theme-attribute variants.

* fix(daemon): model CSS cascade in tracking lint and detect blue→cyan trust gradients

Address PR #225 review feedback (3 comments):

- `letter-spacing` / `font-size` selection now picks the LAST matching
  declaration in the rule body, modeling CSS source-order cascade.
  `.eyebrow { letter-spacing: 0.08em; letter-spacing: 0.02em }` renders
  the noncompliant 0.02em the browser actually shows; the previous
  first-match behaviour silently passed it.
- `extractCssTokens` now records every distinct value seen for a token
  across global theme scopes, and `hasAdequateUppercaseTracking`
  enumerates each combination so a default-theme value below the floor
  cannot be rescued by a scoped override that happened to be parsed
  later (`:root { --caps-tracking: 0.02em }` +
  `[data-theme="dark"] { --caps-tracking: 0.08em }` now fires).
- New `trust-gradient` P0 rule pairs blue/sky tokens against cyan
  tokens in `linear-gradient(...)` bodies so `blue→cyan` two-stop
  trust gradients (documented as a cardinal sin in
  `craft/anti-ai-slop.md`) are actually enforced — both the hex form
  (`linear-gradient(90deg, #3b82f6, #06b6d4)`) and the keyword form
  (`linear-gradient(90deg, blue, cyan)`).

Adds 11 regression tests covering each path (cascade override in
<style> and inline form, font-size cascade shifting the floor, both
orderings of the conflicting-token cascade, the don't-over-fire case
when every theme value clears the floor, hex / keyword / sky variants
of the trust gradient, and the don't-double-fire case when
purple-gradient already caught a mixed gradient).

* fix(daemon): apply per-scope cascade in extractCssTokens

When the same CSS custom property is declared more than once inside a
single rule body (e.g. `:root { --caps-tracking: 0.02em;
--caps-tracking: 0.08em }`), CSS source-order cascade collapses to the
last value; the earlier declaration never reaches any element.
`extractCssTokens` was treating intra-scope duplicates as simultaneous
theme alternatives, so `hasAdequateUppercaseTracking` enumerated the
stale 0.02em and emitted a spurious all-caps-no-tracking finding.

Collapse duplicate token declarations within a rule body to the last
value before merging into the cross-scope distinct-value map. Cross-scope
overrides (separate `:root` and `[data-theme]` rules) remain preserved
as distinct values so the conservative theme-cascade check still fires
when ANY applicable theme renders below the floor.

* fix(daemon): scope tracking lint to innermost rules and per-theme tokens

Restrict the upperRe body alternation to [^{}]* so the regex matches
innermost CSS rules and skips at-rule wrappers — an outer @media or
@supports could otherwise capture as a single rule whose selector was
the at-rule and whose body began with the inner selector token, masking
the same-rule font-size and letting noncompliant tracking on large
headings slip through the lenient inherited-size fallback.

Replace the by-name-distinct-values token map with per-scope token
records and a buildResolvedThemes pass that materializes one effective
map per theme. Paired token declarations now stay paired during
evaluation, so theme variants like :root + [data-theme=dark] no longer
generate cross-theme cartesian pairings (e.g. default-size + dark-track)
that emit false positives on legitimate light/dark themes.

---------

Co-authored-by: looper <looper@open-claude.dev>
2026-05-02 11:00:33 +08:00
github-actions[bot]GitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
bc16a2d5b8 Update docs/assets/github-metrics.svg - [Skip GitHub Action] (#241)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-02 10:47:05 +08:00
85714d58c8 fix: daemon OD_DAEMON_URL uses port 0 instead of actual allocated port (#240)
When tools-dev launches the daemon with OD_PORT=0 (letting the OS pick a
free port), app.listen correctly resolves the actual port in its callback,
but the enclosing closure still references the original `port` parameter
(value 0). This causes OD_DAEMON_URL to be set to http://127.0.0.1:0 in
the environment passed to agent subprocesses, breaking media generation
and any daemon callback that relies on isLocalSameOrigin.

Introduce a mutable `resolvedPort` variable, assigned from `actualPort`
inside the listen callback. All downstream consumers (OD_DAEMON_URL,
isLocalSameOrigin calls) now read the resolved value.

Co-authored-by: yyh <yyh@test.cn>
2026-05-02 10:25:57 +08:00
fl0wandGitHub c472178e65 Create tr.ts (#233)
* Create tr.ts

Turkish language support.

* Update tr.ts

Change from the brown fox testing phrase to a Turkish alternative.

* Update tr.ts

Add proposed fixes
2026-05-02 10:22:52 +08:00
VitaminBFFMandGitHub a263a9c11e fix(daemon): quote agent bin path when spawning with shell:true on Windows (#232)
When `useShell` is true (Windows + .cmd/.bat shim), Node.js spawns the
agent via `cmd.exe /d /s /c "<bin> <escaped-args>"`. Node escapes argv
items but does NOT quote the bin path itself — that is the caller's
responsibility. If `resolvedBin` contains spaces (the common case for
npm shims under user directories with a space in the account name, e.g.
`C:\Users\First Last\AppData\...\claude.CMD`), cmd.exe parses up to the
first space as the command and the rest as arguments, then fails with
`The system cannot find the file 'C:\Users\First'` (or the localized
equivalent). The agent process exits in ~0.1s with code 1 and the chat
surfaces the cmd.exe error verbatim.

The Node DEP0190 deprecation warning that fires alongside the original
spawn is the same root cause documented from the runtime side.

Wrap `resolvedBin` in double quotes when `useShell` is true so the full
path stays a single token. The `/s` flag that Node already passes to
cmd.exe handles the resulting `cmd /d /s /c ""path with spaces" args"`
correctly.
2026-05-02 10:20:51 +08:00
Foximo24andGitHub a4fd4f949f fix(tools-dev): use junction instead of dir symlink on Windows (#231)
ensureWebDevNodeModules() called fs.symlink(target, path, "dir") to
link apps/web/node_modules into the web runtime root. On Windows,
"dir" symlinks require either Administrator rights or Developer Mode
(SeCreateSymbolicLinkPrivilege). Standard non-elevated user accounts
without Developer Mode get EPERM and tools-dev exits before web ever
starts.

Junctions ("junction") are functionally equivalent for directory-only
links on the same volume, work for any user without elevation or
Developer Mode, and are silently treated as plain symlinks on POSIX.
The existing isSymbolicLink() check on the next launch still matches
junctions on Node.

Reproduced on Windows 11 + Node 24 with a non-elevated PowerShell
session and Developer Mode off.
2026-05-02 10:17:43 +08:00
4510c69ba1 feat(tools-dev): add --prod flag and OD_HOST for headless server deployment (#222)
* feat(tools-dev): add --prod flag and OD_HOST for headless server deployment

- Lazy-load electronBinaryPath so daemon+web can start without Electron
- Add --prod flag to tools-dev start/run that sets NODE_ENV, OD_WEB_PROD,
  and OD_WEB_OUTPUT_MODE automatically for production Next.js builds
- Add OD_HOST env var support to daemon and web sidecar bind addresses
  (defaults to 127.0.0.1, set to 0.0.0.0 for remote access)
- Skip next.config.ts distDir override when OD_WEB_PROD=1 so production
  builds resolve the default .next directory

Closes #221

* fix: address PR review — hardcode daemon bind to 127.0.0.1, cache electron path

- Revert OD_HOST from daemon: daemon is a local privileged process and must
  only bind 127.0.0.1. Remote access goes through the web sidecar proxy.
- Web sidecar resolveDaemonOrigin now always uses 127.0.0.1, separated from
  the web bind address (OD_HOST) so the daemon proxy works correctly even
  when the web listener binds 0.0.0.0.
- Add OD_HOST character validation to reject clearly invalid values early.
- Cache electronBinaryPath getter result to avoid repeated require() calls.

---------

Co-authored-by: yamsfeer <yamsfeer@users.noreply.github.com>
2026-05-02 09:27:16 +08:00
laihenyiandGitHub 8438270f74 docs: add Traditional Chinese (繁體中文) README (#194)
Add README.zh-TW.md based on the Simplified Chinese version, converted
via OpenCC s2twp and adjusted to use Taiwan-specific terminology
(備援, 螢幕, 堆疊, 即時, 動態, 計畫, 色票, etc.). Update language
switcher in all existing README variants to include 繁體中文.
2026-05-01 23:44:54 +08:00
513bd4edea feat(web): pick prompt templates (not design systems) for image/video projects (#192)
* feat(web): pick prompt templates (not design systems) for image/video projects

The New Project panel now shows the curated prompt-template gallery in the
image and video tabs instead of the Design System picker. Design systems
only make sense for prototypes, slide decks, templates, and the freeform
"other" canvas — they don't map onto image/video generation.

The picked template's body is editable in-line ("optimize" affordance) and
the (possibly tuned) prompt is snapshotted into ProjectMetadata as
`promptTemplate`. The system-prompt composer surfaces it on every turn as
a stylistic + structural reference for the agent — same shape we already
use for the saved-template flow.

Also rename the right-side gallery tabs from "Image prompts" / "Video
prompts" to "Image templates" / "Video templates" across all locales.

Made-with: Cursor

* fix(prompt-templates): address review — escape fences, error UI, empty hint, tests

Apply the actionable feedback from the code review:

- Security: escape triple-backticks in `metadata.promptTemplate.prompt`
  before interpolating it into the system prompt's fenced block. A user
  who pasted ``` into the editable template body could otherwise close
  the fence and inject free-form instructions for the agent. Apply the
  same fix in both the contracts composer and the daemon mirror.
- UX: surface fetchPromptTemplate failures via an inline error banner
  outside the popover, with a one-click retry button bound to the last
  failed pick. Previously the error toast lived inside the popover and
  vanished as soon as the popover closed.
- UX: show a subtle hint below the prompt textarea when the body is
  empty, so users who clear it on purpose understand the agent will
  have no template reference.
- Defense in depth: gate the `**referenceTemplate**:` metadata bullet
  on a non-empty prompt body in both composers, so a stale title can
  never appear in the system prompt without the body it claims to
  reference.
- Tests: add tests/system-prompt-template.test.ts covering the happy
  path (image + video), the backtick escape, the truncation cap, the
  empty-prompt skip, the non-media kinds, and the missing-source path.
- i18n: add `promptTemplates.retry` and `newproj.promptTemplateBodyEmpty`
  across all 9 locales; backfill the prompt-template picker keys for
  ja, es-ES, de which landed on main after this branch.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-01 23:31:31 +08:00
github-actions[bot]GitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
8df7951cfc Update docs/assets/github-metrics.svg - [Skip GitHub Action] (#228)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-01 23:12:15 +08:00
35c3fe678c feat: add design-brief skill with structured I-Lang input format (#184)
* feat: add design-brief skill with structured I-Lang input format

Adds a new skill that accepts structured design briefs (I-Lang protocol
format or natural language) and generates a concrete DESIGN.md with
resolved color, typography, layout, mood, density, and constraint tokens.

Solves the 'make it professional' ambiguity problem by requiring explicit
dimensions. Falls back to sensible defaults for unspecified dimensions
and transparently reports what was assumed.

Outputs: DESIGN.md + optional brief-preview.html showing token swatches.

* fix: add Responsive Behavior and Do's and Don'ts sections to DESIGN.md template

Addresses Codex review: downstream skills (e.g. saas-landing) read
these sections from DESIGN.md. Missing them causes incomplete context.

* fix: address P2 review feedback

- Remove overly broad 'brief' trigger to prevent misfires on
  'brief me on X' or 'in brief'. Replaced with 'create a design brief'.
- Add Natural language → I-Lang mapping table (20 common phrases)
  so the conversion from vague input to structured dimensions is
  explicit, not a black box.

* fix: address all 8 review findings from lefarcen

1. P3 Example values taxonomy: added clarification that values are
   representative, not exhaustive. Agent accepts semantically clear
   unlisted values, prompts on ambiguous ones.

2. P2 8-dimension justification: added Background section explaining
   dimensions derived from analysis of 71 bundled design systems.
   Documented why animation/responsive/a11y are excluded at brief level.

3. P2 Symbolic → concrete token resolution: added Section 2.1 with
   full mapping table (4 palettes, 3 accents, 3 typography, 3 display,
   3 density levels → concrete hex/font/px values).

4. P3 preview.html structure: added 4-section spec (color swatches,
   typography specimens, spacing ruler, live component preview).

5. P2 Default resolution logic: added Section 2.2 with mood-aware
   default rules for each dimension and static fallback set.

6. P3 design_system.requires inconsistency: changed to requires: true,
   generates: true — this skill generates a DESIGN.md from scratch,
   and downstream skills require it. Added note about existing
   DESIGN.md overwrite/merge prompt.

* fix: clarify typeface limit — mono is utility, exempt from 2-face rule

Resolves contradiction between Do's/Don'ts ('no more than 2') and
DESIGN.md example showing 3 faces (Display + Body + Mono).

* fix: address 3 P3 findings from lefarcen

- Reword 'responsive breakpoints' to 'responsive strategy' in Background,
  note DESIGN.md includes sensible breakpoint defaults
- Simplify 'overwrite or merge' to 'overwrite or skip' (merge undefined)
- Add numbering column to 8-dimension table for scannability

* fix: address owner (mrcfps) review — align with OD design-system contract

1. od.mode: prototype → design-system (this skill produces DESIGN.md)
2. design_system.requires: true → false (generates, not consumes)
3. DESIGN.md template uses canonical 9-section OD headings:
   - Visual Theme & Atmosphere (NEW)
   - Color Palette & Roles (was Color)
   - Typography Rules (was Typography)
   - Component Stylings (was Components)
   - Layout Principles (was Layout)
   - Depth & Elevation (unchanged)
   - Do's and Don'ts (unchanged)
   - Responsive Behavior (unchanged)
   - Agent Prompt Guide (unchanged)
4. sections list in frontmatter updated to match

* fix: address 3 contract consistency issues from owner review

1. Remove output_format enum — always generate both DESIGN.md and
   brief-preview.html. Prevents manifest advertising a preview file
   that may not exist when design_md-only mode is selected.

2. Fix example: typography=inter_for_body → typography=inter.
   The example now matches the token resolution table exactly.

3. Make vocabulary closed. Remove 'derive by analogy' instruction
   that contradicted Section 3's 'do not invent values outside the
   resolution table'. Unknown symbolic values now require user
   clarification instead of agent guessing.

* fix: disambiguate mapping table — default to first option when multiple values listed

* fix: add token mappings for all closed vocabulary values

Added concrete tokens for:
- accent=muted_sage (#84A98C) and accent=slate (#64748B)
- typography=dm_sans (DM Sans)
- display=clash_display (Clash Display)

Every value in the vocabulary table now has a corresponding
token-resolution row in Section 2.1. No dead-end paths.

---------

Co-authored-by: adsorgcn <adsorgcn@users.noreply.github.com>
2026-05-01 23:11:38 +08:00
lakatosandGitHub abdd4119b2 fix(daemon): clamp codex reasoning effort to model-supported values (#223)
* fix(daemon): clamp codex reasoning effort to model-supported values

Codex's CLI accepts `none | minimal | low | medium | high | xhigh`, but
real models support narrower subsets — picking Minimal in OD's settings
made every chat turn fail with `agent exited with code 1` because the
OpenAI Responses API rejects `reasoning.effort=minimal` on the gpt-5.2
through 5.5 family with `unsupported_value`.

Add a client-side clamp that maps the user's pick to one the chosen
model accepts (Minimal -> low for gpt-5.5 family, xhigh -> high for
gpt-5.1, gpt-5.1-codex-mini caps at medium). Default / unknown model
is treated as gpt-5.5 since that's codex's current default. The user's
UI pick stays valid; the wire request gets a level the model can use.

* docs(daemon): clarify clampCodexReasoning unknown-model behavior

Per PR review — make explicit that unknown / future model ids pass
through unclamped on purpose. The server error then surfaces as the
signal that a new model family rule should be added here, rather than
silently downgrading every unrecognized id.

* test(daemon): cover codex reasoning-effort clamp matrix

Adds a table-driven test in apps/daemon/tests/agents.test.ts that drives
clampCodexReasoning through the public buildArgs surface. The 18 cases
cover:

- minimal -> low for default / explicit gpt-5.2-5.5
- low / medium / high pass-through on gpt-5.5
- xhigh -> high for gpt-5.1
- gpt-5.1-codex-mini cap (low/minimal -> medium, high/xhigh -> high)
- unknown / future models pass through unchanged
- 'default' reasoning omits the -c flag entirely
- path-style model ids (vendor/gpt-5.5-foo) clamp correctly

Tests stay at the wire level (asserting on the args array codex.buildArgs
produces), keeping the helper non-exported and matching the rest of the
file's style.

* docs(daemon): clarify gpt-5.1-codex-mini accepts medium / high

Per PR review — the original "caps at `medium`" wording was misleading
because the clamp returns `high` for `high` / `xhigh` inputs, which
contradicts the comment. The model actually accepts `{medium, high}`,
so describing it as "accepts only `medium` / `high`" matches the
function's behavior.

Logic unchanged.
2026-05-01 22:58:34 +08:00
Marc ChanandGitHub 06eac21cd8 Fix metrics workflow protected branch updates (#219)
* fix github metrics workflow for org repo

* fix metrics workflow protected branch updates

* fix metrics workflow review notes

Generated-By: looper 0.3.0 (runner=fixer, agent=openai/gpt-5.5)
2026-05-01 22:45:54 +08:00
Pavel CherkashinandGitHub 8c90ef6f55 fix(daemon): deliver Claude Code prompt via stdin to avoid spawn E2BIG/ENAMETOOLONG (#143)
Claude Code is the last agent that still embeds the composed prompt in
argv (`-p <prompt>`). Composed prompts (system prompt + design system +
skill body + user message) routinely exceed:

  - Linux  : MAX_ARG_STRLEN ~128 KB per argv entry → `spawn E2BIG`
  - Windows: CreateProcess command-line cap ~32 KB direct, ~8 KB via
             `.cmd` shim → `spawn ENAMETOOLONG`

the prompt through `child.stdin` (`promptViaStdin: true`). Claude was
explicitly excluded at the time. #97 mitigated the Windows case for
Claude with a `.od-prompt.md` temp-file fallback, but that path still
leaves a stray file in the project, doesn't trigger on Linux, and is
slower than the stdin route the other agents already use.

This change brings Claude in line with the rest:

  - Drop the prompt from argv; keep the bare `-p` print-mode flag
  - Set `promptViaStdin: true` so server.ts pipes `composed` on stdin
  - Add a regression test that asserts the prompt body never lands in
    argv even at 200 KB

Verified locally that `claude -p --output-format stream-json --verbose`
(no positional prompt) reads the prompt from stdin under the default
`--input-format text`, which has no length cap.

Fixes #82
Refs #100, #52
2026-05-01 22:45:10 +08:00
0e2085e361 feat(prompt-templates): add three Three Kingdoms ARPG Seedance 2.0 video templates (#212)
Adds three curated first-party video prompt templates to
`prompt-templates/video/`, each with a matching MP4 preview and a
first-frame poster JPG under `assets/prompt-templates/video/`.

These are the direct video companions to the three image templates
opened in PR #207 (Three Kingdoms ARPG screenshot set). Each video
clip is a ~10-12s in-engine cinematic action beat that brings the
corresponding still to life; the two surfaces can be served as a
pair (still → video).

### New templates

1. `video-seedance-three-kingdoms-lyubu-yuanmen-archery.json`
   ~10s. Lyu Bu (吕布) at the yuanmen between two armies — draws,
   holds, releases one gold-qi arrow toward a distant halberd.
   Companion to image template
   `game-screenshot-three-kingdoms-lyubu-yuanmen-archery`.

2. `video-seedance-three-kingdoms-guanyu-slaying-yanliang.json`
   ~10s. Guan Yu (关羽) on Red Hare — charges, raises the Green
   Dragon Crescent Blade, executes a single clean diagonal cleave
   past Yan Liang. No blood on-screen (the strike is implied by a
   short gold-into-jade qi flash). Companion to image template
   `game-screenshot-three-kingdoms-guanyu-slaying-yanliang`.

3. `video-seedance-three-kingdoms-zhaoyun-cradle-escape.json`
   ~12s. Zhao Yun (赵云) at Changban — rides with the infant A Dou
   cradled in a red silk sling on his chest, parries one incoming
   saber with a clean PERFECT DODGE, vaults a fallen war-chariot
   and clears a path. Hard safety rail in the prompt: the infant
   must stay visible, peaceful and obviously unharmed in every
   frame. Companion to image template
   `game-screenshot-three-kingdoms-zhaoyun-cradle-escape`.

Each template is tuned for `seedance-2.0`, aspect `16:9`, category
`Cinematic` (reuses the existing category — 17 templates already
live there). Every prompt is explicitly in-engine camera-disciplined
(single continuous beat, no cuts, no slow-motion ramps, no whip pans)
and HUD-free (the companion image template carries the HUD), so
reviewers and downstream users can treat the still + clip as a matched
production pair.

Each template exposes 5-7 `{argument}` slots (reference_image /
environment / hero / weapon / target / mount / qi_color / parry_color
/ infant / attacker as appropriate) so users can restyle the same
shot scaffold for any Three Kingdoms (or, with heavier edits, any
historical battlefield) scene.

### Assets

| File | Size |
| --- | --- |
| `video-seedance-three-kingdoms-lyubu-yuanmen-archery.mp4` | 7.6 MB |
| `video-seedance-three-kingdoms-guanyu-slaying-yanliang.mp4` | 10.3 MB |
| `video-seedance-three-kingdoms-zhaoyun-cradle-escape.mp4` | 11.7 MB |
| 3 × first-frame `-poster.jpg` | 445-527 KB each |

Total: ~30 MB across 3 MP4s, each well under GitHub's 100 MB
single-file limit. Hosted in-repo on `raw.githubusercontent.com`
for a fully self-contained first-party contribution — no external
CDN dependency.

### Verification

- `node -e "JSON.parse(...)"` passes on all three new templates.
- Slugs do not collide with any existing video template on `main`.
- `previewImageUrl` (poster) and `previewVideoUrl` (mp4) both point
  at the new in-repo asset paths.
- `source.repo = nexu-io/open-design`, `license = Apache-2.0`,
  `author = open-design contributors`, so the first-party guard in
  `scripts/import-prompt-templates.mjs` (added in #187) preserves
  these across importer re-runs.

### Why

The companion image PR (#207) gives users a beautiful Three
Kingdoms ARPG still they can generate via gpt-image-2; this PR
closes the loop by letting them turn that still into a believable
~10s in-engine clip via seedance-2.0 — the exact production flow
behind the Black-Myth-Wukong-style ARPG demo reel this workstream
is pitching. Paired still + video templates also double as a clean
reference for how first-party `open-design` contributors should
structure a matched set across the two surfaces (same slug suffix,
`companion-to-image` tag, cross-referenced `reference_image` argument,
same hero / environment language, no HUD in the video so HUD can be
sourced from the still).

Co-authored-by: Tuola Ge <gexingli@refly.ai>
2026-05-01 22:27:38 +08:00
04bf930880 feat(prompt-templates): add otaku-dance choreography breakdown infographic (#209)
Adds one curated first-party image prompt template to
`prompt-templates/image/`, with a matching preview JPG under
`assets/prompt-templates/image/`.

### New template

**`infographic-otaku-dance-choreography-breakdown-gokurakujodo-16-panels.json`**
— A single vertical 2:3 poster composed as a 4x4 grid of 16 connected
square panels, forming a full choreography breakdown chart for the
famous Japanese otaku dance song 極楽浄土 (Gokuraku Jodo).

Each panel shows the same cute half-realistic anime idol girl (pink
twin-tails, sailor-collar school-idol uniform, vivid turquoise eyes)
performing one signature pose from the dance, full-body, on a pastel
pink background with:

- a small Japanese caption strip at the bottom ("両手広げ", "指さし天井",
  "ハート手", … "フィナーレ"),
- a numbered circle (1–16) at the top-left corner,
- a clean 4x4 grid with thin black separators,
- an overall header reading "極楽浄土 振り付け 16連動" at the top.

The template is explicitly engineered as a POSE REFERENCE SHEET for
downstream AI video generation — every silhouette is crisp and
unambiguous, no motion lines or background clutter, identical
character across all 16 cells.

Five `{argument}` slots expose song title, romaji, character design,
background color and header text so the same chart can be restyled
for any choreographed dance song while keeping the 16-pose grid
scaffold unchanged. Tuned for `gpt-image-2`, aspect `2:3`. Category:
`Infographic` (reuses the existing enum value; no new enum).

Carries `source.repo = nexu-io/open-design`, Apache-2.0, author
`open-design contributors`.

### Verification

- `node -e "JSON.parse(require('fs').readFileSync('prompt-templates/image/<file>.json','utf8'))"` passes on the new template.
- Slug does not collide with any existing template on `main`, in the
  companion Three Kingdoms PR (#207), or in the companion anime
  fighting-game PR (#208).
- Preview JPG is 396 KB, comparable to the previews landed in #187.
- `aspect = 2:3` matches the underlying 1024x1536 source render and
  the reference-chart layout.

### Why

The existing `Infographic` category has strong timeline/evolution
content (e.g. `3d-stone-staircase-evolution-infographic`) but no
entries aimed at **motion reference** — the one niche where a static
infographic is essentially a production tool for video/animation work.

This template plugs that gap. The 4x4 grid of pose-cells is a
genre-defining layout for dance choreography sheets (used widely for
idol dance covers on Bilibili / YouTube / TikTok) and, because every
cell is a clean, identical-character full-body silhouette on a flat
background, the sheet doubles as a **ready-to-use pose reference**
for AI video pipelines (image-to-video, pose-guided control, keyframe
choreography). One template, two jobs: a pretty poster and a working
production asset.

Co-authored-by: Tuola Ge <gexingli@refly.ai>
2026-05-01 22:27:07 +08:00
26150c60af feat(prompt-templates): add anime fighting game screenshot template (#208)
Adds one curated first-party image prompt template to
`prompt-templates/image/`, with a matching preview JPG under
`assets/prompt-templates/image/`.

### New template

**`game-screenshot-anime-fighting-game-captain-ryuuga-vs-kaze-renshin.json`** —
A cinematic anime fighting-game key-visual / combat screenshot in the
style of *Street Fighter 6* or *Tekken 8* intro art. Two original
anime-styled male warriors face off at the center of a nighttime
Chinese temple courtyard:

- **Captain Ryuuga (P1)** — shirtless straw-hat pirate with a red
  battle-scar, shark-tooth necklace, skull-pattern sash and a warm
  orange-red fire aura.
- **Kaze Renshin (P2)** — spiky jet-black hair, orange martial-arts gi
  with a single black kanji, charging a crackling blue lightning energy
  sphere between his hands.

The background, lighting and warm-vs-cool color grading follow the
rival-fighter convention of the genre (orange left half, blue right
half). The template ships with a complete fighting-game HUD exposed as
slot-filled `{argument}` placeholders:

- Two horizontal health bars with round central round-timer and
  `ROUND N` label, per-side HP-percent numerals.
- P1 / P2 portrait panels with character name + subtitle + emblem.
- Per-side `HITS COMBO` counters and max gauges (orange-left,
  blue-right).

Ten `{argument}` slots in total make it easy to restyle the matchup
(swap the fighters, backdrop, round number, HP percents, combo counts,
names, titles and emblems) without rebuilding the HUD-and-render
boilerplate. Tuned for `gpt-image-2`, aspect `16:9`. Category: `Game UI`
(reuses the existing enum value introduced in #187; no new enum).

Carries `source.repo = nexu-io/open-design`, Apache-2.0, author
`open-design contributors`.

### Verification

- `node -e "JSON.parse(require('fs').readFileSync('prompt-templates/image/<file>.json','utf8'))"` passes on the new template.
- Slug does not collide with any existing template on `main` or in the
  companion Three Kingdoms screenshot PR.
- Preview JPG is 679 KB, comparable to the previews landed in #187.
- `aspect = 16:9` matches the underlying 1792x1024 source render.

### Why

#187 anchored the `Game UI` category with a Chinese MMO HUD mockup.
The companion Three Kingdoms PR extends the category with AAA ARPG
combat-moment screenshots in the Black Myth: Wukong style. This PR
adds a third flavor — an **anime fighting game VS screen** — completing
a compact but representative seed set for the category:

- MMO HUD (UI-heavy scenic moment)
- AAA ARPG (character-driven cinematic combat)
- Anime fighting game (head-to-head VS key visual)

Together these cover three very different HUD conventions and three
different render styles, so downstream users approaching the Game UI
category have a real breadth of starting points to remix from — not
just one aesthetic.

Co-authored-by: Tuola Ge <gexingli@refly.ai>
2026-05-01 22:26:28 +08:00
975c795827 feat(prompt-templates): add three Three Kingdoms ARPG gameplay screenshot templates (#207)
Adds three curated first-party image prompt templates to
`prompt-templates/image/`, each with a matching preview JPG under
`assets/prompt-templates/image/`.

### New templates

1. **`game-screenshot-three-kingdoms-lyubu-yuanmen-archery.json`** —
   Lü Bu drawing a massive recurved warbow at Yuanmen gate to shoot
   down a distant halberd at ~150 paces. Over-the-shoulder gameplay
   framing with a locked-on target marker and distance readout in the
   HUD. Three `{argument}` slots: character, qi_color, setting.

2. **`game-screenshot-three-kingdoms-guanyu-slaying-yanliang.json`** —
   Guan Yu riding the Red Hare warhorse through a torrential-rain
   battlefield, Blue Dragon Crescent Glaive raised, boss-fight HUD with
   a crimson Elite HP bar over the distant enemy general. Four
   `{argument}` slots: character, mount, weapon, setting.

3. **`game-screenshot-three-kingdoms-zhaoyun-cradle-escape.json`** —
   Zhao Yun at Changbanpo cradling the swaddled infant Liu Chan in his
   left arm and sweeping enemies back with a spear in his right, with
   a dedicated ESCORT protection bar in the HUD and a visible combo
   counter. Four `{argument}` slots: character, child_protected,
   weapon, setting.

All three follow the same in-game screenshot recipe: AAA next-gen ARPG
in the cinematic photoreal style of Black Myth: Wukong (with Elden Ring
for the Zhao Yun piece), Unreal Engine 5 Nanite/Lumen rendering, third
person over-the-shoulder camera, and a complete semi-transparent HUD
(portrait + HP/qi bars, minimap, skill hotbar, stamina ring, lock-on /
boss / escort widgets as appropriate). Each ships with a negative
prompt block tuned for this genre — no watermark, no gibberish glyphs,
no cutscene framing, no first-person view, no modern clothing.

All JSONs carry `source.repo = nexu-io/open-design`, Apache-2.0, author
`open-design contributors`, and follow the shape introduced in #187 so
the importer's first-party preservation logic applies unchanged.

Category: `Game UI` (reuses the existing enum value from #187; no new
enum introduced).

### Verification

- `node -e "JSON.parse(require('fs').readFileSync('prompt-templates/image/<file>.json','utf8'))"` passes on all three new templates.
- Slugs do not collide with any existing template on `main`.
- Preview JPGs are 445-527 KB each, comparable to the previews landed in #187.
- `aspect = 16:9` matches the underlying 1536x1024 source renders.

### Why

#187 established `Game UI` as a category with a single HUD-heavy MMO
mockup of a female swordswoman in a scenic shrine. This PR extends the
category with three **combat-moment** screenshots centered on specific
Three Kingdoms heroes in their most iconic scenes — giving the library
a clear complement (character-driven action beats) to the existing
HUD-demo anchor. The Three Kingdoms theme also opens a reusable
narrative scaffold: the `{character}` / `{weapon}` / `{setting}` slot
pattern lets downstream users regenerate the same HUD-accurate gameplay
framing for other Chinese historical figures (e.g. Yue Fei, Huo Qubing)
or wholly custom warriors, without having to rebuild the HUD-and-render
boilerplate from scratch.

Co-authored-by: Tuola Ge <gexingli@refly.ai>
2026-05-01 22:22:11 +08:00
lefarcenandGitHub 38bdb59d86 fix(release-stable): build desktop before typecheck, drop workspace tests (#216) open-design-v0.1.0 2026-05-01 20:53:10 +08:00
Marc ChanandGitHub 1d6d86fa69 fix github metrics workflow for org repo (#217) 2026-05-01 20:49:22 +08:00
913a6c3ea7 fix(release-stable): build daemon before workspace typecheck (#215)
The verify job ran `pnpm typecheck` (root script) which executes
`pnpm -r run typecheck` *before* the daemon build. The e2e workspace's
typecheck imports types from `apps/daemon/dist/*.js`, so on a fresh
clone (every CI run) it fails with TS2307 cannot-find-module.

Drive the order explicitly inside the verify job:
  1. install deps
  2. build daemon (produces dist/*.js + .d.ts)
  3. workspace typecheck
  4. check:residual-js
  5. workspace tests

This keeps the root `typecheck` script untouched (which other dev /
contributor workflows may depend on) — the workflow simply imposes the
correct order itself. The atomic publish job already prevented orphan
tags/releases when the first dispatch failed at typecheck.

Co-authored-by: Elian <elian@EliandeMacBook-Pro.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 20:26:50 +08:00
lefarcenandGitHub 451ae983db release: Open Design 0.1.0 — first public release (#206) 2026-05-01 20:15:18 +08:00
Siri-RayandGitHub 82a79c8b23 Add desktop chrome header (#205)
* feat: add desktop chrome header

* fix: address app chrome review feedback

Generated-By: looper 0.2.7 (runner=fixer, agent=codex)

* fix: address app chrome review feedback

Generated-By: looper 0.2.7 (runner=fixer, agent=codex)
2026-05-01 19:09:02 +08:00
6789dd4c11 feat(prompt-templates): add dance storyboard and ancient-china MMO HUD templates (#187)
* feat(prompt-templates): add dance storyboard and ancient-china MMO HUD templates

- social-media-post-sensational-girl-dance-storyboard-8-shots: 8-shot storyboard
  prompt set with shared global style tokens, negative prompt, and character
  lock, tuned for GPT-Image-2. Produces a continuous dance choreography as a
  coherent 8-frame sequence.
- game-ui-ancient-china-open-world-mmo-hud: HUD mockup for a Black Myth: Wukong
  style ancient-China open-world MMO, centered on a female swordswoman
  protagonist. Covers character panel, minimap with bagua frame, skill hotbar,
  quest tracker, chat window, and world-space nameplates with Chinese
  typography rules.
- scripts/import-prompt-templates.mjs: preserve hand-authored templates on
  re-run by keeping any JSON whose source.repo is not the upstream CC-BY
  corpus. Previously clearDir wiped the whole directory, which would delete
  first-party curated prompts on the next import.

Both templates carry source attribution to nexu-io/open-design under
Apache-2.0. Categories reuse the existing 'Social Media Post' and 'Game UI'
enum values already present in the gallery.

* feat(prompt-templates): add preview images for dance and MMO HUD templates

- assets/prompt-templates/image/social-media-post-sensational-girl-dance-storyboard-8-shots.jpg:
  an 8-panel storyboard render produced from the template itself, downscaled to
  1024x1536 JPEG for gallery thumbnails.
- assets/prompt-templates/image/game-ui-ancient-china-open-world-mmo-hud.jpg:
  a reference HUD screenshot rendered from the template prompt via gpt-image-2,
  downscaled to 1536x1024 JPEG.
- Both templates now reference these previews via a raw.githubusercontent.com
  URL pointing at nexu-io/open-design main, matching the pattern used by the
  existing YouMind-sourced seeds (cms-assets.youmind.com) so every card in the
  Prompt Gallery carries a thumbnail once the PR merges.

---------

Co-authored-by: Joey <joey@open-design.local>
2026-05-01 17:30:02 +08:00
SidandGitHub 9969003aa5 fix web design files upload picker (#199) 2026-05-01 17:28:14 +08:00
AresdgiandGitHub 59e4966dda feat(version): add app version awareness (#204)
* feat(version): add app version awareness

* fix(version): detect packaged sidecars across platforms
2026-05-01 17:26:54 +08:00
SidandGitHub 6d268eedde fix web locale registry test (#202) 2026-05-01 16:59:29 +08:00
abaae96eb2 update: add 57 DESIGN.md files from awesome-design-skills (#92)
* update: add 57 DESIGN.md files from awesome-design-skills

* Update i18n coverage for added design systems

---------

Co-authored-by: mrcfps <mrc@powerformer.com>
2026-05-01 16:57:25 +08:00
SidandGitHub cbbab86fd0 fix web design files dropzone copy (#200) 2026-05-01 16:57:18 +08:00
PerishFireandGitHub f604ff1ec2 Add Windows beta packaging and release assets (#191) 2026-05-01 16:46:15 +08:00
SidandGitHub dd283fa2d3 fix web design files agent copy (#201) 2026-05-01 16:36:02 +08:00
sysCat64andGitHub ed0b36ed24 feat(i18n): add Japanese locale (#197)
Add `ja` locale with full translation of all UI strings to Japanese.
Register it in types.ts and index.tsx alongside existing locales.
2026-05-01 14:42:00 +08:00