diff --git a/packages/skills/skills/penguin-sdk/SKILL.md b/packages/skills/skills/penguin-sdk/SKILL.md index 6fbf1d1..c943057 100644 --- a/packages/skills/skills/penguin-sdk/SKILL.md +++ b/packages/skills/skills/penguin-sdk/SKILL.md @@ -1,10 +1,10 @@ --- name: penguin-sdk -description: Build AI apps on the Penguin Harness SDK — self-contained projects, the createSession/run streaming loop, and a complete RAG recipe that ingests documents into a knowledge base and answers with citations behind a web UI. +description: Build AI apps on the Penguin Harness SDK — self-contained projects, the createSession/run streaming loop with thinking and image messages, and a complete RAG recipe that ingests documents into a knowledge base and answers with citations behind a web UI. short_description: Build AI and RAG apps on the Penguin Harness SDK. short_description_zh: 基于 Penguin SDK 构建 AI 与 RAG 应用。 -version: 14 -updated: 2026-07-25T00:00:00Z +version: 17 +updated: 2026-07-30T11:10:00Z --- # Penguin Harness SDK @@ -31,23 +31,19 @@ const agent = await createAgent({ root: path.join(import.meta.dirname, "penguin_ With every reference relative to the project, the user can move or copy the folder anywhere and it still runs. -## Before you build: keys and the data root +## Keys and the data root — check before you build -**Important prerequisite — set the key up first, then develop.** For AI-app development, have the user add the model API key in **this agent's key vault** (gear icon on its card, Agents page → settings → key vault tab) *before* you start building, so the credential is in your shell environment when you configure and test the app. Model ids to offer the user can come straight from the penguin CLI catalog (`penguin config model add --help`, and the agenthub-models skill's id table). +**The app's Penguin data root must live inside the CWD workspace — never `~/.penguin`.** Point `createAgent({ root })` and every `penguin config ... --root
`) and sanitize to exactly that whitelist before inserting — never inject unsanitized model output.
+- **Cross-language retrieval**: the corpus and the user often speak different languages (English docs, Chinese questions), and BM25 is purely lexical — a Chinese question scores zero against English chunks. At ingest time derive a small bilingual keyword map for the corpus's core vocabulary (10–20 domain terms, e.g. `权限 → permissions / allow / deny`, `钩子 → hooks`) and expand query tokens through it in `search()` before scoring; keep the per-character CJK tokenizer. The persona already pins the answer language to the question's language.
+
+**Persona** (`persona.md`) — the embedded agent's role, written per the agent-creation skill. Shape: one role sentence ("You are an expert on X; you answer strictly from the provided context blocks"), citation and refusal rules, plain-text output (no Markdown — the output contract above), answer language follows the question.
## Verify before you hand over
@@ -330,4 +354,4 @@ Never declare the app done without running it:
Then `curl` one of the returned source `url`s — it must return the document, not a 404 (citation links have to resolve).
5. Open the UI (or screenshot it) to confirm the layout renders.
-Fix any failure and re-verify. Report with backtick-wrapped relative paths (`server.ts`, `public/index.html`, …), how to start the app, and the assumptions you made.
+Fix any failure and re-verify; when the app accepts image input, one verification question must include a real image. Report with backtick-wrapped relative paths (`server.ts`, `public/index.html`, …), how to start the app, and the assumptions you made.
diff --git a/packages/skills/skills/web-design/SKILL.md b/packages/skills/skills/web-design/SKILL.md
index 4a9b087..2c2584a 100644
--- a/packages/skills/skills/web-design/SKILL.md
+++ b/packages/skills/skills/web-design/SKILL.md
@@ -1,22 +1,33 @@
---
name: web-design
-description: Penguin visual language for generated web pages and app UIs — GitHub-style simplicity with a single blue accent, light and pure-black dark themes, design tokens, and component and chat-interface recipes.
+description: Penguin visual language for generated web pages and app UIs — GitHub-style simplicity with a single blue accent, light and pure-black dark themes, design tokens, component and chat-interface recipes, plus an opt-in warm paper editorial theme.
short_description: Penguin-style visual defaults for generated web UIs.
short_description_zh: 生成网页的 Penguin 风格视觉规范。
-version: 3
-updated: 2026-07-20T15:00:00Z
+version: 6
+updated: 2026-07-30T11:10:00Z
---
# Web Design
-Default visual language for every web page or frontend you generate, distilled from the Penguin Harness landing page and web app. The idea is **GitHub-style simplicity**: solid backgrounds, 1px borders instead of shadows, system fonts, one blue accent used sparingly. Depth comes from hairline borders, not shadows or gradients; dark mode is pure black, not navy. Apply these defaults unless the user explicitly asks for another style.
+Default visual language for every web page or frontend you generate, distilled from the Penguin Harness landing page and web app. The idea is **GitHub-style simplicity**: solid backgrounds, 1px borders instead of shadows, system fonts, one blue accent used sparingly. Depth comes from hairline borders, not shadows or gradients; dark mode is pure black, not navy. Apply these defaults unless the user explicitly asks for another style. One packaged alternative exists — the **paper editorial** theme below, for requests that call for a warm, print-like feel; pick one language per product and never mix them. The typography discipline, language, motion, IME, citation and responsiveness rules apply under both. Treat the user's one-line request as the whole spec: this skill fills every unstated gap, so the result is a finished page — never a wireframe that waits for styling feedback.
## Before you start
-If the user's message only invokes this skill (e.g. "use web-design skill") without a concrete page or interface to build, ask what they want to build. When a concrete build is already requested (an app UI, a landing page, a RAG chat interface), do **not** ask about styling — apply the defaults below.
+If the user's message only invokes this skill (e.g. "use web-design skill") without a concrete page or interface to build, ask what they want to build. When a concrete build is already requested (an app UI, a landing page, a RAG chat interface), do **not** ask about styling — colors, fonts, spacing, radii and layout are all decided by the defaults below; the only question ever worth asking is *what to build*, never *how it should look*. Route by request shape: conversational or docs-QA → the chat/RAG layout; product or marketing → the page layout; tool-like apps → a sticky nav + panels composed from the components.
Non-negotiable for ANY text input that sends on Enter: **never send while an IME composition is in progress** (check `event.isComposing`, falling back to `event.keyCode === 229`, on keydown). For Chinese/Japanese/Korean input methods, that Enter only confirms the composed text — auto-sending on it fires half-typed messages. Details in the composer recipe below.
+## Ship complete
+
+Every delivery, even from a one-line request, includes all of this unasked:
+
+- `` matching the UI language; ``; a real ``.
+- Dark mode wired and persisted when using the default language (the paper theme is light-only); single column under 640px; no horizontal scroll.
+- Every async surface has designed loading, empty, error and success states — never a blank region or a silent failure.
+- A working keyboard path: visible `:focus-visible`, Esc closes overlays (focus returning to the trigger), Enter submits (IME-safe as above).
+- `alt` text on images, `aria-label` on icon-only buttons; tap targets ≥ 40px.
+- Zero external requests: system fonts, inline or local CSS/JS, inline SVG icons — no CDN, no icon font, no analytics.
+
## Design tokens
```css
@@ -82,15 +93,37 @@ One easing everywhere: `var(--ease)`, durations 120–280ms. Entrances rise in (
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
```
+## Opt-in theme: paper editorial
+
+A second complete language — warm paper tones, serif display headings, mono micro-labels — for when the user asks for a warm, editorial, print- or magazine-like feel. Light-only: if the product needs dark mode, use the default language instead.
+
+```css
+:root {
+ color-scheme: light;
+ --paper: #f7f4ee; /* page bg */ --panel: #eeebe4; /* side panels */ --card: #fffdf9;
+ --ink: #262421; --muted: #716d67; --line: #ded9d0; /* warm hairlines */
+ --accent: #d6663f; --accent-deep: #9e462b; /* the only accent; deep tone for text/links */
+ --live: #547567; /* status green — live dots and retrieval notes only */
+}
+```
+
+- **Serif display over sans body** — hero and panel titles use system serifs (`Georgia, "Times New Roman", "Songti SC", "Noto Serif SC", serif`; still no CDN fonts), weight 400–500, `letter-spacing: -.04em`, hero `clamp(38px, 5vw, 62px)`, with exactly one word wrapped in an accent-colored ``. Body text stays on the default sans stack, 13–14px / line-height 1.7.
+- **Mono micro-labels** — eyebrows/kickers, example numbering (`01`), status values and citation numbers: 9–11px uppercase monospace, `letter-spacing: .1–.18em`, in `--accent-deep` or `--muted`. The serif-vs-mono contrast is this theme's hierarchy tool, replacing the default theme's weight-and-size ladder.
+- **Tighter radii, warm shadows** — cards, buttons and source rows use 4–5px radii (pills stay 9999px). Unlike the default language, soft warm-tinted shadows belong here: composer `box-shadow: 0 14px 40px rgb(65 50 39 / .09)`; example-card hover may lift `translateY(-2px)` and gain `0 10px 28px rgb(62 49 40 / .07)`.
+- **Signature shapes** — brand mark: an `--accent` square with one tight corner (`border-radius: 11px 11px 11px 4px`) holding a white serif initial; user chat bubbles echo it with `border-radius: 3px 14px 14px 14px`. Empty-state flourish: that mark in a circular medallion ringed by 1–2 offset half-circle hairlines (`clip-path: inset(0 50% 0 0)`) — this theme's counterpart of the default dot grid.
+- **Buttons** — primary is an `--accent` fill (hover `--accent-deep`; icon-only send buttons go circular); secondary stays a hairline border on `--card`/white. Links use `--accent-deep`.
+
## Chat / RAG app layout
The default shape for a generated conversational or docs-QA app:
-- **Shell** — centered column, `max-width: 48rem`, `padding: 0 16px`; sticky nav on top with the app name; message list grows, composer pinned at the bottom.
-- **Empty state** — vertically centered title + one-line subtitle in `--fg-muted`, over an optional dot-grid backdrop (`background-image: radial-gradient(rgb(26 115 232 / .14) 1px, transparent 1px); background-size: 22px 22px;` faded out with a bottom mask) — the only decorative flourish allowed. Below it, a wrapped row of 3–4 example-question pill chips the app can genuinely answer; clicking one fills and submits the composer.
-- **Messages** — user messages right-aligned in a `--gray-100`/dark `#1f1f1f` rounded bubble (radius 12px, padding 8px 14px, max-width 85%); assistant messages plain on the page background, no bubble. Stream deltas into the assistant message as they arrive with a 1-character pulsing cursor; render markdown.
-- **Citations** — after an answer, a wrapped row of pill chips: `[1] path — heading`, brand-tinted variant. Clicking a chip (or an inline `[n]` in the answer) opens a popover/panel showing the **verbatim original text block** the citation refers to, with a link to open the full source document; a citation that is only a label or only a link is not enough.
-- **Composer** — a bordered card (radius 12px) with a borderless textarea inside and a small primary send button bottom-right; Enter sends, Shift+Enter for newline; disable while streaming. **Never send while an IME composition is in progress**: on keydown, ignore Enter when `event.isComposing` (or `event.keyCode === 229`) — for CJK input methods that Enter only confirms the composed text, and auto-sending on it fires half-typed messages.
+- **Shell** — centered column, `max-width: 48rem`, `padding: 0 16px`; sticky nav on top with the app name; message list grows, composer pinned at the bottom. A docs-QA app whose index is worth showing may add a left **knowledge panel** (grid `310px 1fr` under the nav; panel-toned bg `--gray-50` / paper `--panel`, 1px right border): kicker, display title, one-paragraph description, an index-status block of label-vs-mono-value rows (docs, chunks, last synced, a LIVE dot), and a one-line pipeline (`corpus → retrieval → cited answer`); the chat pane keeps its own centered column. On mobile the panel hides behind an ⓘ button in the nav and drops down fixed beneath it.
+- **Empty state** — a vertically centered composition: uppercase eyebrow (brand / `--accent-deep`) → title with at most one accent word → one-line subtitle in `--fg-muted`, over the theme flourish (default: dot-grid backdrop `background-image: radial-gradient(rgb(26 115 232 / .14) 1px, transparent 1px); background-size: 22px 22px;` faded out with a bottom mask; paper: the orbit medallion) — the only decorative flourish allowed. Below it, 3–4 example questions the app can genuinely answer, as pill chips or as a 2-column grid (1-column mobile) of numbered cards — mono `01` in accent, the question at 13px, a `↗` corner affordance; hover tints the border (paper may also lift 2px). Clicking one fills and submits the composer.
+- **Messages** — user messages right-aligned in a `--gray-100`/dark `#1f1f1f` rounded bubble (radius 12px, padding 8px 14px, max-width 85%); assistant messages plain on the page background, no bubble. Stream deltas into the assistant message as they arrive with a 1-character pulsing cursor. Prefer a plain-text output contract over a Markdown pipeline: when the app controls its model's prompt, instruct plain-text answers and style them directly (escape → blank-line paragraphs → decorate `[n]` markers); build a Markdown renderer only when the output format isn't yours to set, and then escape HTML in the model text before your own transforms — never inject it raw. Retrieval-backed answers may open with a small status line above the text — breathing dot + "已检索 N 个相关片段" / "N sources matched", 11px in `--live`/`--fg-faint` — so evidence visibly precedes prose.
+- **Thinking** — reasoning models may stream a chain of thought before the answer. Give it its own collapsible block above the answer text: a header row ("思考过程" / "Thinking" + chevron + elapsed seconds) over 13px `--fg-muted` content behind a hairline left border, expanded while it streams, auto-collapsed the moment the first answer delta arrives (reopenable). Never restyle thinking as answer prose and never cite from it; when the model emits none, no placeholder space appears.
+- **Citations** — inline `[n]` markers render as small raised accent superscripts (mono, ~10px). After the answer, either surface works, but it must reveal both the **verbatim original text block** and a link to the full source — a citation that is only a label or only a link is not enough: (a) a wrapped row of brand-tinted pill chips `[1] path — heading` opening a popover/panel on click; or (b) an **accordion of source cards** under the answer — each row a tinted numbered circle + doc title + section + `+/−` chevron, expanding to the verbatim excerpt as a mono blockquote (accent left border, max-height ≈150px, scrollable) plus the source link.
+- **Composer** — a bordered card (radius 12px; paper: 4–5px with the warm shadow) holding a borderless textarea and a footer row: kbd hints on the left (`Enter` send · `Shift+Enter` newline, 11px muted, hidden on mobile), the primary send button on the right (paper: circular accent icon button); Enter sends, Shift+Enter for newline; disable while streaming. A short gradient from transparent into the page bg eases the list into the composer zone; below it, one 10–11px muted disclaimer line says what answers are based on. **Never send while an IME composition is in progress**: on keydown, ignore Enter when `event.isComposing` (or `event.keyCode === 229`) — for CJK input methods that Enter only confirms the composed text, and auto-sending on it fires half-typed messages.
+- **Attachments** (when the app accepts images) — a paperclip button plus paste and drag-drop onto the composer; queued images preview above the textarea as 48px rounded thumbnails with a hover `×`; send attaches and clears the queue. In sent user messages, thumbnails render at max-height ~160px, radius 8px, click to view full size.
- **States** — loading: three pulsing dots in `--fg-faint`; error: 13px `#b91c1c` text on `#fef2f2` (dark: `#f87171` on `#450a0a`) in a rounded box with a retry affordance. Never leave a silent failure.
## Page layout (marketing / landing)
diff --git a/packages/web/src/lib/strings-en.ts b/packages/web/src/lib/strings-en.ts
index ace1242..f0aba8e 100644
--- a/packages/web/src/lib/strings-en.ts
+++ b/packages/web/src/lib/strings-en.ts
@@ -590,7 +590,7 @@ export const en: Strings = {
## Index page
- \`index.html\` at the root: a card grid listing all 10 games (name + one-line mechanic + controls), each card opening its game.
-- One design language shared with every game, following the web-design skill; dark/light themes via \`\` remembered in localStorage; responsive, single column on phones.
+- One design language shared with every game, following the web-design skill.
## Wrap-up
- Review as a whole: the 10 mechanics really are distinct, the styling is consistent, and every index link resolves.
@@ -622,7 +622,7 @@ export const en: Strings = {
- Keyboard shortcuts: Space play/pause, ← → previous/next, ↑ ↓ volume
## Design
-Penguin visual style (see the web-design skill), dark/light themes via , dark by default, remembered in localStorage. Responsive: on phones the sidebar becomes a horizontally scrolling top bar.
+Penguin visual style (see the web-design skill), dark by default. On phones the sidebar becomes a horizontally scrolling top bar.
When done, open index.html in a browser and self-test once.`,
},
@@ -635,12 +635,7 @@ When done, open index.html in a browser and self-test once.`,
"the app acts as a Claude Code configuration expert, answering Claude Code questions " +
"with retrieval-augmented replies and clickable citations that reveal the matched " +
"original text chunk and link to the real documents; " +
- "give it a beautiful web chat UI following the web-design skill, with a few example questions in the empty state. " +
- "Pay particular attention to matching the question's language against the corpus: the docs are English, " +
- "while questions will often be Chinese. With a lexical retriever such as BM25, a Chinese query MUST be " +
- "converted to English first (translate it, or extract English keywords) before it reaches the index — " +
- "otherwise not a single Chinese term matches the English index and retrieval silently degrades to nothing. " +
- "Mixed Chinese/English questions must retrieve correctly too, and the answer should follow the language of the question. " +
+ "give it a beautiful web chat UI following the web-design skill. " +
"When done, run the app and self-test one Chinese question and one English question, confirming both retrieve " +
"the right English documents and stream their answers, then tell me how to access it.",
},
diff --git a/packages/web/src/lib/strings.ts b/packages/web/src/lib/strings.ts
index 7f93e5a..5e977a2 100644
--- a/packages/web/src/lib/strings.ts
+++ b/packages/web/src/lib/strings.ts
@@ -574,7 +574,7 @@ export const zh = {
## 索引首页
- 根目录 \`index.html\`:卡片网格列出全部 10 个游戏(名称 + 一句话玩法 + 操作方式),点击进入对应游戏。
-- 与所有游戏共用一套设计语言,遵循 web-design 技能;深色 / 浅色主题(\`\`)并用 localStorage 记忆;响应式,手机端单列。
+- 与所有游戏共用一套设计语言,遵循 web-design 技能。
## 收尾
- 统一验收:10 个游戏玩法确实不重复、风格一致,索引页的链接全部可达。
@@ -606,7 +606,7 @@ export const zh = {
- 键盘快捷键:空格播放暂停、← → 切歌、↑ ↓ 调音量
## 设计
-Penguin 视觉风格(见 web-design 技能),深色/浅色主题(),默认深色,localStorage 记忆。响应式:手机端侧边栏变为顶部横向滚动。
+Penguin 视觉风格(见 web-design 技能),默认深色。手机端侧边栏变为顶部横向滚动。
完成后在浏览器打开 index.html 自测一次。`,
},
@@ -618,11 +618,7 @@ Penguin 视觉风格(见 web-design 技能),深色/浅色主题(