dsh-pixel-ui 1.0.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/client/index.js", "../src/client/pixel.css"],
4
- "sourcesContent": ["/**\n * dsh-pixel-ui — browser half (the pixel skin).\n *\n * Registers four Agent-Xi-style pixel themes on the ThemeService seam\n * (`ctx.theme.register` with --dsw-alias-* token overrides): wood (dark),\n * paper (light), warm (dark), retro-green (dark). The stylesheet carries\n * the pixel borders, fonts, CRT overlay, and component tweaks that alias\n * tokens alone cannot express, scoped to `html[data-pixel-ui]` with a\n * `data-pixel-theme` variant attribute for the four palettes.\n *\n * The host settings scope persists only the built-in preferences\n * (light/dark/system), so this plugin persists the last choice itself\n * (localStorage) and restores it on load; the first run activates the\n * default wood skin. A settings row in the General section switches\n * between the four skins and back to the modern default (`system`).\n *\n * @module dsh-pixel-ui/client\n */\nimport { createElement as h } from 'react'\nimport { defineStore } from '@deepseek-ai/dsh-client-runtime/client'\nimport cssText from './pixel.css'\n\n/** Plugin name; also the patch row id. */\nexport const name = 'dsh-pixel-ui'\n\n/** Required services: the theme runtime and the settings item slot. */\nexport const inject = ['slots', 'theme']\n\n/** localStorage key carrying the last theme choice across loads. */\nconst THEME_STORAGE_KEY = 'dsh-pixel-ui:theme'\n\n/** The four pixel themes (Agent Xi palettes) registered on the ThemeService. */\nconst THEMES = [\n {\n id: 'pixel-wood',\n colorScheme: 'dark',\n tokens: {\n '--dsw-alias-bg-base': '#1A0F06',\n '--dsw-alias-bg-layer-1': '#2C1A0C',\n '--dsw-alias-bg-layer-2': '#3A2515',\n '--dsw-alias-bg-overlay': '#241408',\n '--dsw-alias-border-l1': '#4A3020',\n '--dsw-alias-border-l2': '#8B6B45',\n '--dsw-alias-brand-primary': '#F4D03F',\n '--dsw-alias-label-primary': '#F5E6C8',\n '--dsw-alias-label-secondary': '#9B7B5B',\n '--dsw-alias-state-error-primary': '#E74C3C',\n '--dsw-alias-state-success-primary': '#7DCE82',\n '--dsw-alias-state-warn-primary': '#F39C12',\n '--dsw-specific-sidebar-fill': '#241408',\n },\n },\n {\n id: 'pixel-paper',\n colorScheme: 'light',\n tokens: {\n '--dsw-alias-bg-base': '#F0E8D8',\n '--dsw-alias-bg-layer-1': '#E8DCC8',\n '--dsw-alias-bg-layer-2': '#DDD0B8',\n '--dsw-alias-bg-overlay': '#E2D6BE',\n '--dsw-alias-border-l1': '#D4C4A8',\n '--dsw-alias-border-l2': '#9B8B6B',\n '--dsw-alias-brand-primary': '#C8960C',\n '--dsw-alias-label-primary': '#3A2A10',\n '--dsw-alias-label-secondary': '#6B5020',\n '--dsw-alias-state-error-primary': '#8B2020',\n '--dsw-alias-state-success-primary': '#3A7A40',\n '--dsw-alias-state-warn-primary': '#805000',\n '--dsw-specific-sidebar-fill': '#E8DCC8',\n },\n },\n {\n id: 'pixel-warm',\n colorScheme: 'dark',\n tokens: {\n '--dsw-alias-bg-base': '#1E0E04',\n '--dsw-alias-bg-layer-1': '#34180A',\n '--dsw-alias-bg-layer-2': '#4A2210',\n '--dsw-alias-bg-overlay': '#2A1308',\n '--dsw-alias-border-l1': '#5C2E16',\n '--dsw-alias-border-l2': '#A86B40',\n '--dsw-alias-brand-primary': '#FF8C42',\n '--dsw-alias-label-primary': '#F0C8A0',\n '--dsw-alias-label-secondary': '#A86B40',\n '--dsw-alias-state-error-primary': '#D93B3B',\n '--dsw-alias-state-success-primary': '#6DBF6D',\n '--dsw-alias-state-warn-primary': '#FF8C42',\n '--dsw-specific-sidebar-fill': '#2A1308',\n },\n },\n {\n id: 'pixel-retro',\n colorScheme: 'dark',\n tokens: {\n '--dsw-alias-bg-base': '#0A0E0A',\n '--dsw-alias-bg-layer-1': '#0E140E',\n '--dsw-alias-bg-layer-2': '#121A12',\n '--dsw-alias-bg-overlay': '#0C100C',\n '--dsw-alias-border-l1': '#2A402A',\n '--dsw-alias-border-l2': '#3A5A3A',\n '--dsw-alias-brand-primary': '#33FF33',\n '--dsw-alias-label-primary': '#33FF33',\n '--dsw-alias-label-secondary': '#118811',\n '--dsw-alias-state-error-primary': '#FF3333',\n '--dsw-alias-state-success-primary': '#33FF33',\n '--dsw-alias-state-warn-primary': '#FFCC33',\n '--dsw-specific-sidebar-fill': '#0C100C',\n },\n },\n]\n\nconst PIXEL_IDS = THEMES.map((theme) => theme.id)\n\n/** Built-in preference ids; together with the pixel ids they are restorable. */\nconst RESTORABLE_IDS = new Set(['light', 'dark', 'system', ...PIXEL_IDS])\n\n/** Settings row choices: the four skins plus the modern default escape. */\nconst THEME_CHOICES = [\n { id: 'pixel-wood', label: '像素·木屋', swatch: '#F4D03F' },\n { id: 'pixel-paper', label: '像素·羊皮纸', swatch: '#F5E6C8' },\n { id: 'pixel-warm', label: '像素·暖阳', swatch: '#FF8C42' },\n { id: 'pixel-retro', label: '像素·终端绿', swatch: '#33FF33' },\n { id: 'system', label: '现代默认', swatch: '#7A7A7A' },\n]\n\n/**\n * Declares the theme row slot store: a mirror of the theme service\n * preference, written only by the plugin's apply-world change listener\n * (same shape as the ui-theme Appearance row store).\n * @returns the store handle.\n */\nfunction createThemeRowStore() {\n return defineStore({\n init: () => ({ preference: null, revision: -1 }),\n actions: {\n sync: (draft, preference, revision) => {\n if (revision <= draft.revision) return\n draft.preference = preference\n draft.revision = revision\n },\n },\n })\n}\n\n/**\n * General-section row: switch between the four pixel skins and back to the\n * modern default. Reads selection state through props.useStore and writes\n * through the injected setTheme callback.\n * @param props - slot shares (useStore + injected face).\n * @returns the row element tree.\n */\nfunction ThemeRow(props) {\n const preference = props.useStore((state) => state.preference)\n return h('div', { className: 'px-theme-row' },\n h('div', { className: 'px-theme-row-title' }, '像素主题'),\n h('div', { className: 'px-theme-row-cubes' },\n THEME_CHOICES.map((choice) => h('button', {\n key: choice.id,\n type: 'button',\n className: 'px-theme-btn' + (preference === choice.id ? ' px-theme-btn-active' : ''),\n onClick: () => { props.setTheme(choice.id) },\n },\n h('span', { className: 'px-theme-swatch', style: { background: choice.swatch } }),\n choice.label,\n )),\n ),\n )\n}\n\n/** Client plugin body: register the four themes, restore/persist the choice, inject CSS and the settings row. */\nexport function apply(ctx) {\n // Scope attributes mirror the active theme, so the stylesheet only shades\n // while a pixel theme is selected and the variant rules can pick the palette.\n const syncScope = () => {\n const active = ctx.theme.getTheme().active\n const on = PIXEL_IDS.includes(active.id)\n document.documentElement.toggleAttribute('data-pixel-ui', on)\n if (on) document.documentElement.setAttribute('data-pixel-theme', active.id)\n else document.documentElement.removeAttribute('data-pixel-theme')\n }\n\n // Custom theme ids do not survive the host settings scope, so carry the\n // latest preference here; built-in choices ride along too.\n const persist = (snapshot) => {\n try {\n localStorage.setItem(THEME_STORAGE_KEY, snapshot.preference)\n } catch {\n // Storage unavailable (private mode / quota): persistence is best-effort.\n }\n }\n ctx.on('theme/change', (snapshot) => { syncScope(); persist(snapshot) })\n\n ctx.effect(() => {\n const disposers = THEMES.map((definition) => ctx.theme.register(definition))\n return () => { for (const dispose of disposers) dispose() }\n }, 'dsh-pixel-ui: theme registration')\n\n // Restore the last choice; the first run wears the default wood skin.\n let saved = null\n try { saved = localStorage.getItem(THEME_STORAGE_KEY) } catch { /* storage unavailable */ }\n const target = saved !== null && RESTORABLE_IDS.has(saved) ? saved : 'pixel-wood'\n try { ctx.theme.setTheme(target) } catch { /* unknown id from an older version: keep current */ }\n syncScope()\n\n // Pixel theme row in the General settings section (alongside Appearance).\n const rowStore = createThemeRowStore()\n let bound = undefined\n const syncRow = (snapshot) => { bound?.sync(snapshot.preference, snapshot.revision) }\n ctx.on('theme/change', syncRow)\n ctx.slots.inject('settings.general.item', () => ctx.slots.register({\n name: 'settings.general.item',\n id: 'pixel-theme',\n order: 20,\n store: rowStore,\n inject: (actions) => {\n bound = actions\n // Re-sync from the getter so no event is lost between registration and\n // first render (the store's revision guard drops stale duplicates).\n syncRow(ctx.theme.getTheme())\n return { setTheme: (id) => { ctx.theme.setTheme(id) } }\n },\n }, ThemeRow))\n\n ctx.effect(() => {\n const style = document.createElement('style')\n style.dataset.plugin = 'dsh-pixel-ui'\n style.textContent = cssText\n document.head.appendChild(style)\n return () => {\n style.remove()\n document.documentElement.removeAttribute('data-pixel-ui')\n document.documentElement.removeAttribute('data-pixel-theme')\n }\n }, 'dsh-pixel-ui: stylesheet')\n}\n", "/* dsh-pixel-ui — 星露谷风像素皮肤(木屋/羊皮纸/暖阳/终端绿四主题)\r\n * 全面对标 Agent Xi 前端像素语言(web/src/styles/main.css):\r\n * - 木色/金色/羊皮纸三系色板 + 阶梯硬阴影 + 内凹/凸起 bevel\r\n * - 像素字体做界面装饰(标题/页签/按钮/徽章/输入框),正文与代码走 mono\r\n * - 木纹条纹纹理、CRT 扫描线 + 暗角 + 微闪烁、像素光标、像素按压动效\r\n * 作用域 html[data-pixel-ui]:任一像素主题激活时生效,\r\n * data-pixel-theme 区分四种配色。切回 现代默认/浅色/深色 即整体退出。\r\n * 原则:颜色尽量交给主题 token,本表负责 token 表达不了的像素质感。 */\r\n\r\n@font-face {\r\n font-family: 'Fusion Pixel 12';\r\n src: url('/dsh-pixel-ui/fonts/fusion-pixel-12px.woff2') format('woff2');\r\n font-weight: 400;\r\n font-style: normal;\r\n font-display: swap;\r\n}\r\n@font-face {\r\n font-family: 'Press Start 2P';\r\n src: url('/dsh-pixel-ui/fonts/PressStart2P-Regular.ttf') format('truetype');\r\n font-weight: 400;\r\n font-style: normal;\r\n font-display: swap;\r\n}\r\n\r\nhtml[data-pixel-ui] {\r\n /* ── Xi 同款色板 ─────────────────────────────────────── */\r\n --px-wood-darkest: #1A0F06;\r\n --px-wood-dark: #2C1A0C;\r\n --px-wood-mid: #3A2515;\r\n --px-wood-warm: #4A3020;\r\n --px-wood-light: #6B4C2E;\r\n --px-wood-pale: #8B6B45;\r\n --px-gold: #F4D03F;\r\n --px-gold-bright: #FFE066;\r\n --px-gold-dark: #C99A00;\r\n --px-gold-deep: #8B6B00;\r\n --px-parchment: #F5E6C8;\r\n --px-text-on-dark: #E8D5B0;\r\n --px-text-light: #9B7B5B;\r\n\r\n /* 输入框专用色(各主题单独覆盖,保证任何底色下文字可见) */\r\n --px-input-bg: #1A0F06;\r\n --px-input-text: #FFFDF5;\r\n --px-input-caret: #F4D03F;\r\n --px-input-ph: #9B7B5B;\r\n\r\n /* 文字阴影强度:暗色主题用 2px 像素硬阴影,浅色主题收敛防重影 */\r\n --px-ts-chrome: 2px 2px 0 rgba(26, 15, 6, 0.6), -1px -1px 0 rgba(26, 15, 6, 0.4);\r\n --px-ts-btn: 1px 1px 0 rgba(0, 0, 0, 0.4);\r\n\r\n /* 像素字体栈:中文/全角走 fusion-pixel(12px 原生),英文数字兜底 Press Start 2P */\r\n --px-font: 'Fusion Pixel 12', 'Press Start 2P', monospace;\r\n --px-mono: 'Cascadia Code', 'Consolas', 'Courier New', monospace;\r\n}\r\n\r\n/* ── 四主题色板(Agent Xi 同款)────────────────────────── */\r\nhtml[data-pixel-theme='pixel-paper'] {\r\n --px-wood-darkest: #F0E8D8;\r\n --px-wood-dark: #E8DCC8;\r\n --px-wood-mid: #DDD0B8;\r\n --px-wood-warm: #D4C4A8;\r\n --px-wood-light: #B8A080;\r\n --px-wood-pale: #9B8B6B;\r\n --px-gold: #C8960C;\r\n --px-gold-bright: #E0A800;\r\n --px-gold-dark: #A07000;\r\n --px-gold-deep: #805000;\r\n --px-parchment: #FFFDF5;\r\n --px-text-on-dark: #3A2A10;\r\n --px-text-light: #8B7040;\r\n --px-input-bg: #FFFDF5;\r\n --px-input-text: #3A2A10;\r\n --px-input-caret: #C8960C;\r\n --px-input-ph: #8B7040;\r\n /* 浅色底上 2px 硬阴影会变重影:去掉文字阴影,靠字体本身 */\r\n --px-ts-chrome: none;\r\n --px-ts-btn: none;\r\n}\r\nhtml[data-pixel-theme='pixel-warm'] {\r\n --px-wood-darkest: #1E0E04;\r\n --px-wood-dark: #34180A;\r\n --px-wood-mid: #4A2210;\r\n --px-wood-warm: #5C2E16;\r\n --px-wood-light: #8B4A28;\r\n --px-wood-pale: #A86B40;\r\n --px-gold: #FF8C42;\r\n --px-gold-bright: #FFA64D;\r\n --px-gold-dark: #D96E30;\r\n --px-gold-deep: #A05020;\r\n --px-parchment: #FFF0E0;\r\n --px-text-on-dark: #F0C8A0;\r\n --px-text-light: #A86B40;\r\n --px-input-bg: #1E0E04;\r\n --px-input-text: #FFFBF5;\r\n --px-input-caret: #FF8C42;\r\n --px-input-ph: #A86B40;\r\n}\r\nhtml[data-pixel-theme='pixel-retro'] {\r\n --px-wood-darkest: #0A0E0A;\r\n --px-wood-dark: #0E140E;\r\n --px-wood-mid: #121A12;\r\n --px-wood-warm: #162016;\r\n --px-wood-light: #2A402A;\r\n --px-wood-pale: #3A5A3A;\r\n --px-gold: #33FF33;\r\n --px-gold-bright: #66FF66;\r\n --px-gold-dark: #00CC00;\r\n --px-gold-deep: #009900;\r\n --px-parchment: #0A1A0A;\r\n --px-text-on-dark: #33FF33;\r\n --px-text-light: #118811;\r\n --px-input-bg: #0A0E0A;\r\n --px-input-text: #33FF33;\r\n --px-input-caret: #33FF33;\r\n --px-input-ph: #118811;\r\n}\r\n\r\n/* ── 全局质感:禁次像素平滑(像素字体的灵魂)、位图三件套、方形化 ── */\r\nhtml[data-pixel-ui] body {\r\n -webkit-font-smoothing: none;\r\n -moz-osx-font-smoothing: grayscale;\r\n text-rendering: geometricPrecision;\r\n font-family: var(--px-font) !important;\r\n letter-spacing: 0.01em;\r\n}\r\n\r\nhtml[data-pixel-ui] img,\r\nhtml[data-pixel-ui] canvas,\r\nhtml[data-pixel-ui] video {\r\n image-rendering: -moz-crisp-edges;\r\n image-rendering: crisp-edges;\r\n image-rendering: pixelated;\r\n}\r\n\r\n/* 一切皆方角(像素风第一原则) */\r\nhtml[data-pixel-ui] * {\r\n border-radius: 0 !important;\r\n}\r\n\r\nhtml[data-pixel-ui] ::selection {\r\n background: var(--px-gold);\r\n color: var(--px-wood-darkest);\r\n}\r\n\r\nhtml[data-pixel-ui] :focus-visible {\r\n outline: 2px solid var(--px-gold) !important;\r\n outline-offset: 1px;\r\n}\r\n\r\n/* ── 像素滚动条:方形、木色 ────────────────────────────── */\r\nhtml[data-pixel-ui] ::-webkit-scrollbar { width: 10px; height: 10px; }\r\nhtml[data-pixel-ui] ::-webkit-scrollbar-track {\r\n background: var(--px-wood-darkest);\r\n border-left: 2px solid var(--px-wood-dark);\r\n}\r\nhtml[data-pixel-ui] ::-webkit-scrollbar-thumb {\r\n background: var(--px-wood-light);\r\n border: 2px solid var(--px-wood-darkest);\r\n}\r\nhtml[data-pixel-ui] ::-webkit-scrollbar-thumb:hover { background: var(--px-wood-pale); }\r\n\r\n/* ── 排版:装饰性 UI 用像素字体,正文/代码用 mono(Xi 的信息分层) ── */\r\nhtml[data-pixel-ui] h1,\r\nhtml[data-pixel-ui] h2,\r\nhtml[data-pixel-ui] h3,\r\nhtml[data-pixel-ui] [class*='title'],\r\nhtml[data-pixel-ui] [class*='label'],\r\nhtml[data-pixel-ui] [class*='tab'],\r\nhtml[data-pixel-ui] [class*='badge'],\r\nhtml[data-pixel-ui] [class*='chip'],\r\nhtml[data-pixel-ui] [class*='Tag'],\r\nhtml[data-pixel-ui] [class*='pill'] {\r\n font-family: var(--px-font) !important;\r\n text-shadow: var(--px-ts-chrome);\r\n}\r\n\r\n/* 标题/页签取金色(Xi .panel-title 语义) */\r\nhtml[data-pixel-ui] h1,\r\nhtml[data-pixel-ui] h2,\r\nhtml[data-pixel-ui] h3,\r\nhtml[data-pixel-ui] [class*='title'] {\r\n color: var(--px-gold-bright) !important;\r\n letter-spacing: 0.02em;\r\n}\r\n\r\n/* 页签:选中态金色下划线 */\r\nhtml[data-pixel-ui] [class*='tab'],\r\nhtml[data-pixel-ui] [aria-selected='true'] {\r\n font-size: 12px;\r\n}\r\nhtml[data-pixel-ui] [class*='tab'][class*='active'],\r\nhtml[data-pixel-ui] [aria-selected='true'] {\r\n box-shadow: inset 0 -3px 0 var(--px-gold) !important;\r\n}\r\n\r\n/* 徽章/小标签:切角 + 内凹高光(小元素才有资格切角,避免大块裁阴影) */\r\nhtml[data-pixel-ui] [class*='badge'],\r\nhtml[data-pixel-ui] [class*='chip'],\r\nhtml[data-pixel-ui] [class*='Tag'],\r\nhtml[data-pixel-ui] [class*='pill'] {\r\n font-size: 11px;\r\n line-height: 1.5;\r\n border: 1px solid #000 !important;\r\n box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.3) !important;\r\n clip-path: polygon(\r\n 0 2px, 2px 2px, 2px 0,\r\n calc(100% - 2px) 0, calc(100% - 2px) 2px, 100% 2px,\r\n 100% calc(100% - 2px), calc(100% - 2px) calc(100% - 2px), calc(100% - 2px) 100%,\r\n 2px 100%, 2px calc(100% - 2px), 0 calc(100% - 2px)\r\n );\r\n}\r\n\r\n/* ── 正文/消息/代码:mono 内容层(Xi log 区用 Fira Code 的忠实移植) ── */\r\nhtml[data-pixel-ui] [class*='message'],\r\nhtml[data-pixel-ui] [class*='bubble'],\r\nhtml[data-pixel-ui] [class*='markdown'],\r\nhtml[data-pixel-ui] [class*='content'],\r\nhtml[data-pixel-ui] pre,\r\nhtml[data-pixel-ui] code,\r\nhtml[data-pixel-ui] [class*='code'] {\r\n font-family: var(--px-mono) !important;\r\n letter-spacing: 0;\r\n text-shadow: none;\r\n}\r\n\r\n/* 消息行:左侧木色竖条(Xi .log-row 色带,统一木色) */\r\nhtml[data-pixel-ui] [class*='message'],\r\nhtml[data-pixel-ui] [class*='bubble'] {\r\n border-left: 3px solid var(--px-wood-warm) !important;\r\n}\r\n\r\n/* 代码块:黑底木边(Xi .log-code-wrap) */\r\nhtml[data-pixel-ui] pre,\r\nhtml[data-pixel-ui] [class*='code'] {\r\n background: var(--px-wood-darkest) !important;\r\n color: var(--px-text-on-dark) !important;\r\n border: 2px solid var(--px-wood-light) !important;\r\n box-shadow: inset 2px 2px 0 rgba(0, 0, 0, 0.4) !important;\r\n}\r\n/* 行内代码:暗底金字 */\r\nhtml[data-pixel-ui] code {\r\n background: rgba(0, 0, 0, 0.4) !important;\r\n color: var(--px-gold-bright) !important;\r\n border: 1px solid rgba(0, 0, 0, 0.12) !important;\r\n padding: 0 4px;\r\n}\r\nhtml[data-pixel-ui] pre code {\r\n background: transparent !important;\r\n color: inherit !important;\r\n border: none !important;\r\n padding: 0;\r\n}\r\n\r\n/* ── 按钮:8-bit 凸起 + 阶梯硬阴影 + 悬停抬升 + 按压沉底 ── */\r\nhtml[data-pixel-ui] button,\r\nhtml[data-pixel-ui] [role='button'],\r\nhtml[data-pixel-ui] [class*='btn'],\r\nhtml[data-pixel-ui] [class*='Button'] {\r\n font-family: var(--px-font) !important;\r\n font-size: 12px;\r\n letter-spacing: 0.02em;\r\n border: 2px solid #000 !important;\r\n box-shadow:\r\n inset 2px 2px 0 rgba(255, 248, 231, 0.22),\r\n inset -2px -2px 0 rgba(0, 0, 0, 0.5),\r\n 2px 2px 0 #000,\r\n 3px 3px 0 rgba(0, 0, 0, 0.6) !important;\r\n text-shadow: var(--px-ts-btn);\r\n transition: transform 0.08s steps(2), box-shadow 0.08s steps(2), filter 0.08s steps(2);\r\n}\r\nhtml[data-pixel-ui] button:hover,\r\nhtml[data-pixel-ui] [role='button']:hover,\r\nhtml[data-pixel-ui] [class*='btn']:hover,\r\nhtml[data-pixel-ui] [class*='Button']:hover {\r\n filter: brightness(1.1);\r\n transform: translate(-2px, -2px);\r\n box-shadow:\r\n inset 2px 2px 0 rgba(255, 248, 231, 0.25),\r\n inset -2px -2px 0 rgba(0, 0, 0, 0.4),\r\n 4px 4px 0 #000,\r\n 5px 5px 0 rgba(0, 0, 0, 0.6) !important;\r\n}\r\nhtml[data-pixel-ui] button:active,\r\nhtml[data-pixel-ui] [role='button']:active,\r\nhtml[data-pixel-ui] [class*='btn']:active,\r\nhtml[data-pixel-ui] [class*='Button']:active {\r\n transform: translate(2px, 2px);\r\n box-shadow:\r\n inset 2px 2px 0 rgba(0, 0, 0, 0.5),\r\n inset -2px -2px 0 rgba(255, 248, 231, 0.1) !important;\r\n}\r\n\r\n/* 主按钮(品牌色)金边 */\r\nhtml[data-pixel-ui] button[class*='primary'],\r\nhtml[data-pixel-ui] [class*='btn-primary'] {\r\n border-color: var(--px-gold-deep) !important;\r\n box-shadow:\r\n inset 2px 2px 0 rgba(255, 224, 102, 0.5),\r\n inset -2px -2px 0 rgba(0, 0, 0, 0.5),\r\n 2px 2px 0 #000,\r\n 3px 3px 0 rgba(0, 0, 0, 0.6) !important;\r\n}\r\n\r\n/* ── 输入框:黑木底 + 羊皮纸文字 + 金色光标(修复文字不可见) ──\r\n * 关键:必须显式给文字颜色。Xi #command-input:cream 字 / wood-darkest 底 /\r\n * gold-deep 边 / 3px 内凹,聚焦金边光环。 */\r\nhtml[data-pixel-ui] input,\r\nhtml[data-pixel-ui] textarea,\r\nhtml[data-pixel-ui] select,\r\nhtml[data-pixel-ui] [contenteditable='true'] {\r\n font-family: var(--px-font) !important;\r\n font-size: 12px;\r\n letter-spacing: 0;\r\n color: var(--px-input-text) !important;\r\n -webkit-text-fill-color: var(--px-input-text) !important;\r\n caret-color: var(--px-input-caret) !important;\r\n background-color: var(--px-input-bg) !important;\r\n border: 2px solid var(--px-gold-deep) !important;\r\n box-shadow:\r\n inset 3px 3px 0 rgba(0, 0, 0, 0.6),\r\n inset -1px -1px 0 rgba(244, 208, 63, 0.1),\r\n 0 0 0 1px #000 !important;\r\n}\r\nhtml[data-pixel-ui] input:focus,\r\nhtml[data-pixel-ui] textarea:focus,\r\nhtml[data-pixel-ui] select:focus,\r\nhtml[data-pixel-ui] [contenteditable='true']:focus {\r\n border-color: var(--px-gold) !important;\r\n box-shadow:\r\n inset 3px 3px 0 rgba(0, 0, 0, 0.6),\r\n 0 0 0 2px var(--px-gold),\r\n 0 0 8px rgba(244, 208, 63, 0.4) !important;\r\n}\r\nhtml[data-pixel-ui] input::placeholder,\r\nhtml[data-pixel-ui] textarea::placeholder {\r\n color: var(--px-input-ph) !important;\r\n -webkit-text-fill-color: var(--px-input-ph) !important;\r\n opacity: 1;\r\n}\r\n/* 输入框容器只做边框质感,不压背景(避免把容器文字也染黑) */\r\nhtml[data-pixel-ui] [class*='input'] {\r\n border-color: var(--px-gold-deep) !important;\r\n}\r\n\r\n/* ── 面板/卡片/弹层:双框 + 阶梯硬阴影 + 木纹竖条 ────────── */\r\nhtml[data-pixel-ui] [class*='panel'],\r\nhtml[data-pixel-ui] [class*='card'],\r\nhtml[data-pixel-ui] [class*='Card'],\r\nhtml[data-pixel-ui] dialog,\r\nhtml[data-pixel-ui] [role='dialog'],\r\nhtml[data-pixel-ui] [class*='modal'],\r\nhtml[data-pixel-ui] [class*='popover'],\r\nhtml[data-pixel-ui] [class*='menu'],\r\nhtml[data-pixel-ui] [class*='dropdown'],\r\nhtml[data-pixel-ui] [class*='popup'] {\r\n border: 2px solid var(--px-wood-warm) !important;\r\n outline: 2px solid #000;\r\n outline-offset: -2px;\r\n box-shadow:\r\n inset 1px 1px 0 rgba(255, 255, 255, 0.08),\r\n inset -1px -1px 0 rgba(0, 0, 0, 0.4),\r\n 2px 2px 0 #000,\r\n 3px 3px 0 rgba(0, 0, 0, 0.6) !important;\r\n /* Xi 木纹:每 10px 一条 1px 暗竖纹 */\r\n background-image:\r\n repeating-linear-gradient(\r\n 90deg,\r\n transparent 0px,\r\n transparent 10px,\r\n rgba(0, 0, 0, 0.06) 10px,\r\n rgba(0, 0, 0, 0.06) 11px\r\n ) !important;\r\n}\r\n\r\n/* 卡片悬停:抬升 + 阴影加厚(Xi hover 位移) */\r\nhtml[data-pixel-ui] [class*='card']:hover,\r\nhtml[data-pixel-ui] [class*='Card']:hover {\r\n transform: translate(-2px, -2px);\r\n box-shadow:\r\n inset 1px 1px 0 rgba(255, 255, 255, 0.1),\r\n inset -1px -1px 0 rgba(0, 0, 0, 0.4),\r\n 4px 4px 0 #000,\r\n 5px 5px 0 rgba(0, 0, 0, 0.6) !important;\r\n}\r\n\r\n/* ── 顶栏/侧栏:木纹渐变 + 黑底边 + 金色发丝 ─────────────── */\r\nhtml[data-pixel-ui] [class*='header'],\r\nhtml[data-pixel-ui] [class*='topbar'],\r\nhtml[data-pixel-ui] [class*='titlebar'],\r\nhtml[data-pixel-ui] [class*='sidebar'],\r\nhtml[data-pixel-ui] [class*='Sidebar'] {\r\n background-image: linear-gradient(180deg, var(--px-wood-warm) 0%, var(--px-wood-dark) 100%) !important;\r\n border-bottom: 3px solid #000 !important;\r\n box-shadow:\r\n inset 0 2px 0 rgba(244, 208, 63, 0.1),\r\n inset 0 -2px 0 rgba(0, 0, 0, 0.5) !important;\r\n}\r\nhtml[data-pixel-ui] [class*='sidebar'],\r\nhtml[data-pixel-ui] [class*='Sidebar'] {\r\n border-right: 3px solid #000 !important;\r\n box-shadow:\r\n inset -3px 0 0 rgba(0, 0, 0, 0.4),\r\n inset 2px 0 0 rgba(255, 255, 255, 0.05) !important;\r\n}\r\n\r\n/* ── 链接:金色下划线 ──────────────────────────────────── */\r\nhtml[data-pixel-ui] a {\r\n color: var(--px-gold-bright) !important;\r\n text-decoration-thickness: 2px;\r\n text-underline-offset: 3px;\r\n}\r\n\r\n/* ── 进度条:刻度纹理 + 硬框(Xi .quest-progress) ──────── */\r\nhtml[data-pixel-ui] [class*='progress'] {\r\n border: 2px solid #000 !important;\r\n box-shadow: inset 2px 2px 0 rgba(0, 0, 0, 0.6) !important;\r\n background-image: repeating-linear-gradient(\r\n 90deg,\r\n transparent 0px,\r\n transparent 4px,\r\n rgba(0, 0, 0, 0.35) 4px,\r\n rgba(0, 0, 0, 0.35) 5px\r\n ) !important;\r\n}\r\n\r\n/* ── 像素光标(SVG data-URI 阶梯箭头,Xi 同款造型) ─────── */\r\nhtml[data-pixel-ui] body {\r\n cursor: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'%3E%3Cpath d='M2 2v17h3v-3h3v-3h3v-3h3v-3h3V2z' fill='%23FFFDF5' stroke='%231A0F06' stroke-width='2'/%3E%3C/svg%3E\") 2 2, auto;\r\n}\r\nhtml[data-pixel-ui] button,\r\nhtml[data-pixel-ui] [role='button'],\r\nhtml[data-pixel-ui] a,\r\nhtml[data-pixel-ui] [class*='card'],\r\nhtml[data-pixel-ui] [class*='tab'],\r\nhtml[data-pixel-ui] [class*='menu'],\r\nhtml[data-pixel-ui] [class*='dropdown'],\r\nhtml[data-pixel-ui] select,\r\nhtml[data-pixel-ui] label[for] {\r\n cursor: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'%3E%3Cpath d='M2 2v17h3v-3h3v-3h3v-3h3v-3h3V2z' fill='%23F4D03F' stroke='%231A0F06' stroke-width='2'/%3E%3C/svg%3E\") 2 2, pointer;\r\n}\r\nhtml[data-pixel-ui] input,\r\nhtml[data-pixel-ui] textarea,\r\nhtml[data-pixel-ui] [contenteditable='true'] {\r\n cursor: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'%3E%3Cpath d='M5 3h4v2h2v2h2v2h2v2h2v2h2v6h-2v2h-4v2H5z' fill='%23FFFDF5' stroke='%231A0F06' stroke-width='2'/%3E%3C/svg%3E\") 4 3, text;\r\n}\r\n\r\n/* ── CRT 覆盖层:扫描线 + 暗角 + 微闪烁(Xi v3 滤镜) ────\r\n * 盖在最顶层但不拦截任何事件;尊重系统“减少动态效果”偏好。 */\r\nhtml[data-pixel-ui] body::after {\r\n content: \"\";\r\n position: fixed;\r\n inset: 0;\r\n z-index: 2147483647;\r\n pointer-events: none;\r\n background:\r\n repeating-linear-gradient(\r\n 0deg,\r\n rgba(0, 0, 0, 0.1) 0px,\r\n rgba(0, 0, 0, 0.1) 1px,\r\n transparent 1px,\r\n transparent 3px\r\n ),\r\n radial-gradient(ellipse at center, transparent 62%, rgba(0, 0, 0, 0.22) 100%);\r\n animation: px-crt-flicker 4s steps(60) infinite;\r\n}\r\n@keyframes px-crt-flicker {\r\n 0%, 100% { opacity: 1; }\r\n 50% { opacity: 0.96; }\r\n}\r\n@media (prefers-reduced-motion: reduce) {\r\n html[data-pixel-ui] body::after { animation: none; }\r\n html[data-pixel-ui] button,\r\n html[data-pixel-ui] [class*='btn'],\r\n html[data-pixel-ui] [class*='Button'],\r\n html[data-pixel-ui] [class*='card'] {\r\n transition: none;\r\n }\r\n}\r\n\r\n/* ── 设置页「像素主题」行(不跟随皮肤作用域:现代模式下也保持可点) ── */\r\n.px-theme-row {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n padding: 12px 0;\r\n border-top: 1px solid rgba(139, 107, 69, 0.35);\r\n}\r\n.px-theme-row-title {\r\n font-family: 'Fusion Pixel 12', 'Press Start 2P', monospace;\r\n font-size: 12px;\r\n color: #F4D03F;\r\n text-shadow: var(--px-ts-chrome, 2px 2px 0 rgba(26, 15, 6, 0.6));\r\n letter-spacing: 0.05em;\r\n}\r\n.px-theme-row-cubes {\r\n display: flex;\r\n flex-wrap: wrap;\r\n gap: 8px;\r\n}\r\n.px-theme-btn {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 5px 10px;\r\n font-family: 'Fusion Pixel 12', 'Press Start 2P', monospace;\r\n font-size: 11px;\r\n color: #E8D5B0;\r\n background: #2C1A0C;\r\n border: 2px solid #1A0F06;\r\n border-radius: 0 !important;\r\n cursor: pointer;\r\n box-shadow:\r\n inset 1px 1px 0 rgba(255, 248, 231, 0.15),\r\n inset -1px -1px 0 rgba(0, 0, 0, 0.4),\r\n 2px 2px 0 rgba(0, 0, 0, 0.5);\r\n}\r\n.px-theme-btn:hover {\r\n filter: brightness(1.12);\r\n transform: translate(-1px, -1px);\r\n}\r\n.px-theme-btn:active {\r\n transform: translate(1px, 1px);\r\n box-shadow: inset 2px 2px 0 rgba(0, 0, 0, 0.4);\r\n}\r\n.px-theme-btn-active {\r\n color: #1A0F06;\r\n background: #F4D03F;\r\n border-color: #8B6B00;\r\n box-shadow:\r\n inset 1px 1px 0 #FFE066,\r\n inset -1px -1px 0 #8B6B00,\r\n 2px 2px 0 rgba(0, 0, 0, 0.5);\r\n}\r\n.px-theme-swatch {\r\n width: 10px;\r\n height: 10px;\r\n border: 1px solid #000;\r\n box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.4);\r\n}\r\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA,mBAAmC;AACnC,oBAA4B;;;ACnB5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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",
6
- "names": ["h"]
4
+ "sourcesContent": ["/**\n * dsh-pixel-ui — browser half (the pixel skin).\n *\n * Registers four Agent-Xi-style pixel themes on the ThemeService seam\n * (`ctx.theme.register` with --dsw-alias-* token overrides): wood (dark),\n * paper (light), warm (dark), retro-green (dark). The stylesheet carries\n * the pixel borders, fonts, CRT overlay, and component tweaks that alias\n * tokens alone cannot express, scoped to `html[data-pixel-ui]` with a\n * `data-pixel-theme` variant attribute for the four palettes.\n *\n * Theme-choice memory is two-layered on purpose:\n * 1. the browser `localStorage` mirror (instant, per-origin);\n * 2. the host-side durable preference (`/dsh-pixel-ui/preference`,\n * stored under $DSH_HOME) which survives DSH Desktop's per-launch\n * random local port — the origin (port included) changes on every\n * restart, so the mirror alone forgets the choice.\n * The host file is authoritative whenever the mirror is absent (new port);\n * explicit user picks are written to both layers at once.\n *\n * The host settings scope persists only the built-in preferences\n * (light/dark/system), so switching model / reasoning-effort re-applies that\n * built-in and would visibly drop the skin. We keep the user's intended\n * theme and re-assert a pixel theme whenever a built-in arrives without an\n * explicit choice in the settings row below (restore must be a user action,\n * not an app-forced re-init).\n *\n * @module dsh-pixel-ui/client\n */\nimport { createElement as h } from 'react'\nimport { defineStore } from '@deepseek-ai/dsh-client-store'\nimport cssText from './pixel.css'\n\n/** Plugin name; also the patch row id. */\nexport const name = 'dsh-pixel-ui'\n\n/** Required services: the theme runtime, the settings item slot, the locale. */\nexport const inject = ['slots', 'theme', 'locale']\n\n/** localStorage key carrying the per-origin theme mirror. */\nconst THEME_STORAGE_KEY = 'dsh-pixel-ui:theme'\n\n/** Host-backed durable preference route (registered by the node half). */\nconst PREFERENCE_URL = '/dsh-pixel-ui/preference'\n\n/**\n * Page-bootstrap global written by the node half's index injection. DSH\n * Desktop renders from `dsh-app://app/`, where the relative fetch above can\n * never reach the loopback route, so the injection is the transport that\n * exists on both deployments.\n */\nconst BOOTSTRAP_GLOBAL = '__DSH_PIXEL_UI__'\n\n/** Default skin for a first run (no mirror, no durable preference). */\nconst DEFAULT_SKIN = 'pixel-wood'\n\n/** The four pixel themes (Agent Xi palettes) registered on the ThemeService. */\nconst THEMES = [\n {\n id: 'pixel-wood',\n colorScheme: 'dark',\n tokens: {\n '--dsw-alias-bg-base': '#1A0F06',\n '--dsw-alias-bg-layer-1': '#2C1A0C',\n '--dsw-alias-bg-layer-2': '#3A2515',\n '--dsw-alias-bg-overlay': '#241408',\n '--dsw-alias-border-l1': '#4A3020',\n '--dsw-alias-border-l2': '#8B6B45',\n '--dsw-alias-brand-primary': '#F4D03F',\n '--dsw-alias-label-primary': '#F5E6C8',\n '--dsw-alias-label-secondary': '#AC8A63',\n '--dsw-alias-state-error-primary': '#E74C3C',\n '--dsw-alias-state-success-primary': '#7DCE82',\n '--dsw-alias-state-warn-primary': '#F39C12',\n '--dsw-alias-state-idle-primary': '#7A5C3E',\n '--dsw-specific-sidebar-fill': '#241408',\n },\n },\n {\n id: 'pixel-paper',\n colorScheme: 'light',\n tokens: {\n '--dsw-alias-bg-base': '#F0E8D8',\n '--dsw-alias-bg-layer-1': '#E8DCC8',\n '--dsw-alias-bg-layer-2': '#DDD0B8',\n '--dsw-alias-bg-overlay': '#E2D6BE',\n '--dsw-alias-border-l1': '#D4C4A8',\n '--dsw-alias-border-l2': '#7A6A4A',\n '--dsw-alias-brand-primary': '#8A6400',\n '--dsw-alias-label-primary': '#3A2A10',\n '--dsw-alias-label-secondary': '#6B5020',\n '--dsw-alias-state-error-primary': '#8B2020',\n '--dsw-alias-state-success-primary': '#3A7A40',\n '--dsw-alias-state-warn-primary': '#805000',\n '--dsw-alias-state-idle-primary': '#847458',\n '--dsw-specific-sidebar-fill': '#E8DCC8',\n },\n },\n {\n id: 'pixel-warm',\n colorScheme: 'dark',\n tokens: {\n '--dsw-alias-bg-base': '#1E0E04',\n '--dsw-alias-bg-layer-1': '#34180A',\n '--dsw-alias-bg-layer-2': '#4A2210',\n '--dsw-alias-bg-overlay': '#2A1308',\n '--dsw-alias-border-l1': '#5C2E16',\n '--dsw-alias-border-l2': '#A86B40',\n '--dsw-alias-brand-primary': '#FF8C42',\n '--dsw-alias-label-primary': '#F0C8A0',\n '--dsw-alias-label-secondary': '#C08A5C',\n '--dsw-alias-state-error-primary': '#D93B3B',\n '--dsw-alias-state-success-primary': '#6DBF6D',\n '--dsw-alias-state-warn-primary': '#FF8C42',\n '--dsw-alias-state-idle-primary': '#8A5A38',\n '--dsw-specific-sidebar-fill': '#2A1308',\n },\n },\n {\n id: 'pixel-retro',\n colorScheme: 'dark',\n tokens: {\n '--dsw-alias-bg-base': '#0A0E0A',\n '--dsw-alias-bg-layer-1': '#0E140E',\n '--dsw-alias-bg-layer-2': '#121A12',\n '--dsw-alias-bg-overlay': '#0C100C',\n '--dsw-alias-border-l1': '#2A402A',\n '--dsw-alias-border-l2': '#4A7A4A',\n '--dsw-alias-brand-primary': '#33FF33',\n '--dsw-alias-label-primary': '#33FF33',\n '--dsw-alias-label-secondary': '#22AA22',\n '--dsw-alias-state-error-primary': '#FF3333',\n '--dsw-alias-state-success-primary': '#33FF33',\n '--dsw-alias-state-warn-primary': '#FFCC33',\n '--dsw-alias-state-idle-primary': '#359035',\n '--dsw-specific-sidebar-fill': '#0C100C',\n },\n },\n]\n\nconst PIXEL_IDS = THEMES.map((theme) => theme.id)\n\n/** Built-in preference ids; together with the pixel ids they are restorable. */\nconst RESTORABLE_IDS = new Set(['light', 'dark', 'system', ...PIXEL_IDS])\n\n/** Settings-row copy (zh is the key-set source of truth; en mirrors it). */\nconst LOCALE_NS = 'settings.pixel-ui'\nconst MESSAGES = {\n zh: {\n 'row.title': '像素主题',\n 'row.desc': '选择后立即生效并跨重启记忆;「现代默认」回到宿主外观',\n 'theme.pixel-wood': '像素·木屋',\n 'theme.pixel-paper': '像素·羊皮纸',\n 'theme.pixel-warm': '像素·暖阳',\n 'theme.pixel-retro': '像素·终端绿',\n 'theme.system': '现代默认',\n },\n en: {\n 'row.title': 'Pixel theme',\n 'row.desc': 'Applies instantly and persists across restarts; “Modern default” returns to the host appearance',\n 'theme.pixel-wood': 'Pixel · Wood',\n 'theme.pixel-paper': 'Pixel · Paper',\n 'theme.pixel-warm': 'Pixel · Warm',\n 'theme.pixel-retro': 'Pixel · Terminal Green',\n 'theme.system': 'Modern default',\n },\n}\n\n/** Settings row choices: the four skins plus the modern default escape. */\nconst THEME_CHOICES = [\n { id: 'pixel-wood', swatch: '#F4D03F' },\n { id: 'pixel-paper', swatch: '#FFFDF5' },\n { id: 'pixel-warm', swatch: '#FF8C42' },\n { id: 'pixel-retro', swatch: '#33FF33' },\n { id: 'system', swatch: '#7A7A7A' },\n]\n\n/** Fallback translator for environments where the slot props carry no `t`. */\nconst fallbackT = (key) => key\n\n/**\n * Declares the theme row slot store: a mirror of the theme service\n * preference, written only by the plugin's apply-world change listener\n * (same shape as the ui-theme Appearance row store).\n * @returns the store handle.\n */\nfunction createThemeRowStore() {\n return defineStore({\n init: () => ({ preference: null, revision: -1 }),\n actions: {\n sync: (draft, preference, revision) => {\n if (revision <= draft.revision) return\n draft.preference = preference\n draft.revision = revision\n },\n },\n })\n}\n\n/**\n * General-section row: switch between the four pixel skins and back to the\n * modern default. Reads selection state through props.useStore, localized\n * copy through props.t, and writes through the injected setTheme callback.\n * Each button carries aria-pressed so the current pick is announced, and the\n * swatch is decorative (the visible label names the theme).\n * @param props - slot shares (useStore, t + injected face).\n * @returns the row element tree.\n */\nfunction ThemeRow(props) {\n const preference = props.useStore((state) => state.preference)\n const t = typeof props.t === 'function' ? props.t : fallbackT\n return h('div', { className: 'px-theme-row', role: 'group', 'aria-label': t('row.title') },\n h('div', { className: 'px-theme-row-title' }, t('row.title')),\n h('div', { className: 'px-theme-row-desc' }, t('row.desc')),\n h('div', { className: 'px-theme-row-cubes' },\n THEME_CHOICES.map((choice) => h('button', {\n key: choice.id,\n type: 'button',\n className: 'px-theme-btn' + (preference === choice.id ? ' px-theme-btn-active' : ''),\n 'aria-pressed': preference === choice.id,\n onClick: () => { props.setTheme(choice.id) },\n },\n h('span', { className: 'px-theme-swatch', 'aria-hidden': 'true', style: { background: choice.swatch } }),\n t(`theme.${choice.id}`),\n )),\n ),\n )\n}\n\n/**\n * Read the per-origin mirror; storage failures read as unset. Values outside\n * the restorable set (stale ids from an older install, corrupt data) read as\n * unset so a first run can still reach the default skin.\n * @returns the stored restorable id or null.\n */\nfunction readLocalTheme() {\n try {\n const value = localStorage.getItem(THEME_STORAGE_KEY)\n return RESTORABLE_IDS.has(value) ? value : null\n } catch { return null }\n}\n\n/**\n * Write the per-origin mirror; storage failures are non-fatal.\n * @param id - theme id to mirror.\n */\nfunction writeLocalTheme(id) {\n try { localStorage.setItem(THEME_STORAGE_KEY, id) } catch { /* best-effort */ }\n}\n\n/**\n * Read the host-side durable preference from the index-injection bootstrap\n * (desktop transport). Values outside the restorable set read as unset.\n * @returns the restorable id or null.\n */\nfunction readBootTheme() {\n try {\n const theme = globalThis[BOOTSTRAP_GLOBAL]?.theme\n return typeof theme === 'string' && RESTORABLE_IDS.has(theme) ? theme : null\n } catch { return null }\n}\n\n/**\n * Read the host-side durable preference over the HTTP route (Web transport).\n * Any failure (desktop `dsh-app://` origin, old host without the route,\n * offline, non-JSON) reads as unset so the local mirror stays usable.\n * @returns the restorable id or null.\n */\nasync function readHostTheme() {\n const injected = readBootTheme()\n if (injected !== null) return injected\n try {\n const res = await fetch(PREFERENCE_URL, { headers: { accept: 'application/json' } })\n if (!res.ok) return null\n const body = await res.json()\n const theme = body?.theme\n return typeof theme === 'string' && RESTORABLE_IDS.has(theme) ? theme : null\n } catch {\n return null\n }\n}\n\n/**\n * Write the host-side durable preference (fire-and-forget): a failed PUT\n * only costs the cross-port memory, never the current session.\n * @param id - theme id to persist.\n */\nfunction writeHostTheme(id) {\n try {\n fetch(PREFERENCE_URL, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ theme: id }),\n }).catch(() => { /* host unavailable: local mirror still holds */ })\n } catch { /* fetch missing: ignore */ }\n}\n\n/** Client plugin body: register the four themes, restore/persist the choice, inject CSS, locale and the settings row. */\nexport function apply(ctx) {\n // Scope attributes mirror the active theme, so the stylesheet only shades\n // while a pixel theme is selected and the variant rules can pick the palette.\n const syncScope = () => {\n const active = ctx.theme.getTheme().active\n const on = PIXEL_IDS.includes(active.id)\n document.documentElement.toggleAttribute('data-pixel-ui', on)\n if (on) document.documentElement.setAttribute('data-pixel-theme', active.id)\n else document.documentElement.removeAttribute('data-pixel-theme')\n return on\n }\n\n // True only for the moment the user picks a theme in the settings row below.\n let explicit = false\n // True from `apply` until the restore below has written the intended skin.\n // While it is up, a built-in preference arriving is the Host's own initial\n // state (settings scope load, theme registration publish) and must not win\n // over the skin the user actually chose.\n let restoring = true\n\n ctx.on('theme/change', (snapshot) => {\n syncScope()\n const pref = snapshot.preference\n if (explicit) {\n explicit = false\n writeLocalTheme(pref)\n writeHostTheme(pref)\n return\n }\n if (PIXEL_IDS.includes(pref)) {\n writeLocalTheme(pref)\n writeHostTheme(pref)\n return\n }\n const want = readLocalTheme()\n if (want !== null && PIXEL_IDS.includes(want)) {\n // Host reapplied a built-in (model / reasoning switch, or its own\n // startup preference): re-assert the skin the user chose.\n try { ctx.theme.setTheme(want) } catch { /* ignore */ }\n return\n }\n if (restoring || want === null) {\n // No recorded choice yet: the built-in arriving is the Host's own\n // initial state. Recording it here would poison the restore below into\n // modern UI, so skip (the restore writes the intended skin itself).\n return\n }\n // A genuine built-in choice (e.g. user clicked Modern default): adopt it.\n writeLocalTheme(pref)\n writeHostTheme(pref)\n })\n\n ctx.effect(() => {\n const disposers = THEMES.map((definition) => ctx.theme.register(definition))\n return () => { for (const dispose of disposers) dispose() }\n }, 'dsh-pixel-ui: theme registration')\n\n // Restore the last choice. The per-origin mirror paints instantly; when it\n // is absent the injected / routed durable preference is the memory, so the\n // first setTheme waits for it instead of flashing the default skin.\n const applyTarget = (target) => {\n try { ctx.theme.setTheme(target) } catch { /* unknown id: keep current */ }\n syncScope()\n writeLocalTheme(target)\n return ctx.theme.getTheme().active.id === target\n }\n\n const restore = async () => {\n const local = readLocalTheme()\n if (local !== null) {\n applyTarget(local)\n const host = await readHostTheme()\n if (host !== null && host !== ctx.theme.getTheme().preference) applyTarget(host)\n return\n }\n const host = await readHostTheme()\n // A fresh install has no memory anywhere: pick the default skin, and retry\n // once because a Host that applies its own preference late would otherwise\n // leave the page in modern UI until the next interaction.\n if (!applyTarget(host ?? DEFAULT_SKIN)) {\n await new Promise((resolve) => { setTimeout(resolve, 120) })\n applyTarget(host ?? DEFAULT_SKIN)\n }\n }\n\n // `restoring` must drop the moment the intended skin is applied — not when\n // the durable read settles. A Host preference landing inside that window\n // (settings scope load, theme registration publish) would otherwise be\n // adopted as a \"real\" choice and silently drop the skin.\n const settle = () => { restoring = false }\n void restore().then(settle, settle)\n\n ctx.effect(() => ctx.locale.register(LOCALE_NS, MESSAGES), 'dsh-pixel-ui: settings row dictionaries')\n\n // Pixel theme row in the General settings section (alongside Appearance).\n const rowStore = createThemeRowStore()\n let bound = undefined\n const syncRow = (snapshot) => { bound?.sync(snapshot.preference, snapshot.revision) }\n ctx.on('theme/change', syncRow)\n ctx.slots.inject('settings.general.item', () => ctx.slots.register({\n name: 'settings.general.item',\n id: 'pixel-theme',\n order: 20,\n store: rowStore,\n locale: LOCALE_NS,\n inject: (actions) => {\n bound = actions\n // Re-sync from the getter so no event is lost between registration and\n // first render (the store's revision guard drops stale duplicates).\n syncRow(ctx.theme.getTheme())\n return { setTheme: (id) => { explicit = true; ctx.theme.setTheme(id) } }\n },\n }, ThemeRow))\n\n ctx.effect(() => {\n const style = document.createElement('style')\n style.dataset.plugin = 'dsh-pixel-ui'\n style.textContent = cssText\n document.head.appendChild(style)\n return () => {\n style.remove()\n document.documentElement.removeAttribute('data-pixel-ui')\n document.documentElement.removeAttribute('data-pixel-theme')\n }\n }, 'dsh-pixel-ui: stylesheet')\n}\n", "/* dsh-pixel-ui — 星露谷风像素皮肤(木屋/羊皮纸/暖阳/终端绿四主题)\n *\n * 设计原则(v1.5.0 重写,依据是桌面端实测):\n *\n * 1. **完全平面(flat)**。用户 2026-10-01 的决定:不要立体效果。\n * 所以本表里**没有任何 box-shadow / text-shadow**——凸起、凹陷、浮雕、\n * 文字投影全部取消。像素的层次改由三样东西表达:\n * ① 整像素描边(2px,内容层 1px);\n * ② 平涂色块之间的一档明度差(主题色板自带);\n * ③ 2px 棋盘抖动(--px-dither)与手绘 SVG 像素图形。\n * 历史上用半透明 inset/outset 做 bevel,在 1.25 DPR 上会渲染成亚像素\n * 灰阶毛边,那正是用户最早报的「伪像素」。\n *\n * 2. **只做 token 表达不了的事**。颜色、背景、文字色交给主题 token;本表负责\n * 方角、2px 整像素描边、像素字体、位图缩放、像素光标、像素滚动条。\n *\n * 3. **选择器必须是「确定的类型」,不许再用 `[class*='card']` 这种子串扫射**。\n * v1.4 的 `[class*=...]` 规则命中了宿主大量它没打算命中的元素(`.xz4KEq_*`、\n * `.Dc7zOa_*`、`.Hqq-bq_*`、图标类…),结果整个界面被盖上一层厚重黑框,\n * 读起来像「处处凹陷的印章」——用户报的「伪像素 / 凹陷」就是它。\n * 这里只保留三类选择器:\n * a) 真正的 HTML 语义(button/input/table/pre/hr/…);\n * b) 稳定的 ARIA 与 data 属性([role=tab]、[aria-selected]、[data-theme]);\n * c) 本插件自己的类名(.px-*)与宿主主题 token(--dsw-*)。\n *\n * 4. **1px 细线只在内容层出现**(表格内线),装饰框一律 2px 起:\n * 像素风里 1px 装饰线在非整数缩放下必然糊成灰线。\n *\n * 作用域 html[data-pixel-ui]:任一像素主题激活时生效,data-pixel-theme 区分配色。\n */\n\n@font-face {\n font-family: 'Fusion Pixel 12';\n src: url('/dsh-pixel-ui/fonts/fusion-pixel-12px.woff2') format('woff2');\n font-weight: 400;\n font-style: normal;\n font-display: swap;\n}\n@font-face {\n font-family: 'Press Start 2P';\n src: url('/dsh-pixel-ui/fonts/PressStart2P-Regular.ttf') format('truetype');\n font-weight: 400;\n font-style: normal;\n font-display: swap;\n}\n\n/* ── 基础令牌(木屋=暗色基准,其余主题覆盖配色) ─────────────────── */\nhtml[data-pixel-ui] {\n /* 木色阶 */\n --px-wood-darkest: #1A0F06;\n --px-wood-dark: #2C1A0C;\n --px-wood-mid: #3A2515;\n --px-wood-warm: #4A3020;\n --px-wood-light: #6B4C2E;\n --px-wood-pale: #8B6B45;\n /* 金色阶 */\n --px-gold: #F4D03F;\n --px-gold-bright: #FFE066;\n --px-gold-dark: #C99A00;\n --px-gold-deep: #8B6B00;\n --px-parchment: #F5E6C8;\n --px-text-on-dark: #E8D5B0;\n --px-text-light: #AC8A63;\n /* 输入件 */\n --px-input-bg: #1A0F06;\n --px-input-text: #FFFDF5;\n --px-input-caret: #F4D03F;\n --px-input-ph: #B08A5E;\n /* 代码 */\n --px-code-bg: #241408;\n --px-code-ink: #FFE066;\n /* 提示框 */\n --px-tooltip-bg: #1A0F06;\n /* 强调面 */\n --px-accent-bg: #F4D03F;\n --px-accent-ink: #1A0F06;\n --px-ink-on-mid: #F5E6C8;\n /* 描边色:平面皮肤唯一的「线」色,2px 整像素 */\n --px-line: #140B04;\n /* 纹理:2px 棋盘抖动 */\n --px-dither: #241408;\n /* 暗角(CRT 只保留暗角,扫描线在非整数 DPR 上必糊) */\n --px-vignette: rgba(0, 0, 0, 0.2);\n /* 内容列宽:宿主把 --dsh-conversation-column-width 写死成 0,\n * clamp(680px, 0 * .64, 920px) 恒取 680px 下限 → 宽屏上正文只占中间一条\n * (踩坑 #7)。这里补上那个变量应有的值:正文随视口撑开。 */\n --px-chat-width: clamp(680px, calc(64vw - 140px), 1200px);\n /* 侧栏 / 顶栏底 */\n --px-sidebar-fill: #241408;\n --px-topbar-fill: #2C1A0C;\n /* 字体栈 */\n --px-font: 'Fusion Pixel 12', 'Press Start 2P', monospace;\n --px-mono: 'Cascadia Code', 'Consolas', 'Courier New', monospace;\n}\n\n/* ── 四主题配色(Agent Xi 同款) ─────────────────────────────────── */\nhtml[data-pixel-theme='pixel-paper'] {\n --px-wood-darkest: #F0E8D8;\n --px-wood-dark: #E8DCC8;\n --px-wood-mid: #DDD0B8;\n --px-wood-warm: #D4C4A8;\n --px-wood-light: #B8A080;\n --px-wood-pale: #9B8B6B;\n --px-gold: #A07000;\n --px-gold-bright: #6B4F00;\n --px-gold-dark: #8A6400;\n --px-gold-deep: #7A5A00;\n --px-parchment: #FFFDF5;\n --px-text-on-dark: #3A2A10;\n --px-text-light: #6B5020;\n --px-input-bg: #FFFDF5;\n --px-input-text: #3A2A10;\n --px-input-caret: #8A6400;\n --px-input-ph: #8B7040;\n --px-code-bg: #E0D4BC;\n --px-code-ink: #5C4400;\n --px-tooltip-bg: #2A1B08;\n --px-accent-bg: #7A5A00;\n --px-accent-ink: #FFFDF5;\n --px-ink-on-mid: #3A2A10;\n --px-line: #8B7040;\n --px-dither: #D8C9AC;\n --px-vignette: rgba(0, 0, 0, 0.07);\n --px-sidebar-fill: #E8DCC8;\n --px-topbar-fill: #E8DCC8;\n}\nhtml[data-pixel-theme='pixel-warm'] {\n --px-wood-darkest: #1E0E04;\n --px-wood-dark: #34180A;\n --px-wood-mid: #4A2210;\n --px-wood-warm: #5C2E16;\n --px-wood-light: #8B4A28;\n --px-wood-pale: #A86B40;\n --px-gold: #FF8C42;\n --px-gold-bright: #FFA64D;\n --px-gold-dark: #D96E30;\n --px-gold-deep: #A05020;\n --px-parchment: #FFF0E0;\n --px-text-on-dark: #F0C8A0;\n --px-text-light: #C08A5C;\n --px-input-bg: #1E0E04;\n --px-input-text: #FFFBF5;\n --px-input-caret: #FF8C42;\n --px-input-ph: #C08A5C;\n --px-code-bg: #2A1308;\n --px-code-ink: #FFA64D;\n --px-tooltip-bg: #1E0E04;\n --px-accent-bg: #FF8C42;\n --px-accent-ink: #1E0E04;\n --px-ink-on-mid: #FFF0E0;\n --px-line: #150801;\n --px-dither: #2A1308;\n --px-sidebar-fill: #2A1308;\n --px-topbar-fill: #34180A;\n}\nhtml[data-pixel-theme='pixel-retro'] {\n --px-wood-darkest: #0A0E0A;\n --px-wood-dark: #0E140E;\n --px-wood-mid: #121A12;\n --px-wood-warm: #162016;\n --px-wood-light: #2A402A;\n --px-wood-pale: #3A5A3A;\n --px-gold: #33FF33;\n --px-gold-bright: #66FF66;\n --px-gold-dark: #00CC00;\n --px-gold-deep: #009900;\n --px-parchment: #B8F0B8;\n --px-text-on-dark: #33FF33;\n --px-text-light: #22AA22;\n --px-input-bg: #0A0E0A;\n --px-input-text: #33FF33;\n --px-input-caret: #33FF33;\n --px-input-ph: #22AA22;\n --px-code-bg: #0E140E;\n --px-code-ink: #66FF66;\n --px-tooltip-bg: #0A0E0A;\n --px-accent-bg: #33FF33;\n --px-accent-ink: #0A0E0A;\n --px-ink-on-mid: #B8F0B8;\n --px-line: #040704;\n --px-dither: #101A10;\n --px-sidebar-fill: #0C100C;\n --px-topbar-fill: #0E140E;\n}\n\n/* ── 内容列宽(桌面端「一半一半」的修正,踩坑 #7) ──────────────────\n * 宿主只在样式表里声明 `--dsh-chat-content-width: var(--dsh-chat-user-width,\n * clamp(680px, calc(var(--dsh-conversation-column-width, 0px) * .64), 920px))`,\n * 而 `--dsh-conversation-column-width` 全仓无人赋值 → clamp 恒取 680px。\n * 在根作用域用 !important 把两个变量补上(宿主会把 user-width 以内联样式写在\n * 容器上,只有 !important 能压过内联)。 */\nhtml[data-pixel-ui] {\n --dsh-conversation-column-width: calc(100vw - 280px) !important;\n --dsh-chat-user-width: var(--px-chat-width) !important;\n --dsh-chat-content-width: var(--px-chat-width) !important;\n}\n/* 兜底:万一宿主把列宽写死在自己的样式层,这里同权重覆盖。 */\nhtml[data-pixel-ui] [class*='xz4KEq_column'] {\n max-width: var(--px-chat-width);\n}\n\n/* ── 全局:方角、禁次像素平滑、位图缩放 ─────────────────────────── */\nhtml[data-pixel-ui] * {\n border-radius: 0 !important;\n}\nhtml[data-pixel-ui] body {\n -webkit-font-smoothing: none;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: geometricPrecision;\n font-family: var(--px-font) !important;\n /* 0.01em 会把字形推到半像素 → 灰边;像素字体必须整像素落位 */\n letter-spacing: 0;\n}\nhtml[data-pixel-ui] img,\nhtml[data-pixel-ui] canvas,\nhtml[data-pixel-ui] video {\n image-rendering: pixelated;\n}\nhtml[data-pixel-ui] ::selection {\n background: var(--px-accent-bg);\n color: var(--px-accent-ink);\n}\nhtml[data-pixel-ui] :focus-visible {\n outline: 2px solid var(--px-gold) !important;\n outline-offset: 1px;\n}\n\n/* 暗角:只保留径向暗角(扫描线在 1.25 DPR 上必糊成灰膜)。 */\nhtml[data-pixel-ui] body::after {\n content: \"\";\n position: fixed;\n inset: 0;\n z-index: 2147483647;\n pointer-events: none;\n background: radial-gradient(ellipse at center, transparent 70%, var(--px-vignette) 100%);\n}\n@media (prefers-reduced-motion: reduce) {\n html[data-pixel-ui] * {\n transition: none !important;\n animation: none !important;\n }\n}\n\n/* ── 像素滚动条:方、10px、木色 ─────────────────────────────────── */\nhtml[data-pixel-ui] {\n scrollbar-color: var(--px-wood-light) var(--px-wood-darkest);\n}\nhtml[data-pixel-ui] ::-webkit-scrollbar {\n width: 10px;\n height: 10px;\n}\nhtml[data-pixel-ui] ::-webkit-scrollbar-track {\n background: var(--px-wood-darkest);\n border-left: 2px solid var(--px-wood-dark);\n}\nhtml[data-pixel-ui] ::-webkit-scrollbar-thumb {\n background: var(--px-wood-light);\n border: 2px solid var(--px-wood-darkest);\n}\nhtml[data-pixel-ui] ::-webkit-scrollbar-thumb:hover {\n background: var(--px-wood-pale);\n}\nhtml[data-pixel-ui] ::-webkit-scrollbar-corner {\n background: var(--px-wood-darkest);\n}\n\n/* ── 排版:装饰性文字走像素字体;正文/代码走 mono ────────────────── */\nhtml[data-pixel-ui] h1,\nhtml[data-pixel-ui] h2,\nhtml[data-pixel-ui] h3,\nhtml[data-pixel-ui] h4,\nhtml[data-pixel-ui] summary,\nhtml[data-pixel-ui] button,\nhtml[data-pixel-ui] label,\nhtml[data-pixel-ui] legend,\nhtml[data-pixel-ui] [role='tab'],\nhtml[data-pixel-ui] [role='menuitem'],\nhtml[data-pixel-ui] [role='tooltip'],\nhtml[data-pixel-ui] [role='switch'] {\n font-family: var(--px-font) !important;\n}\nhtml[data-pixel-ui] h1,\nhtml[data-pixel-ui] h2,\nhtml[data-pixel-ui] h3 {\n color: var(--px-gold-bright) !important;\n}\n/* 正文/代码层:mono、不加字阴影、长 token 允许断行(像素字体更宽) */\nhtml[data-pixel-ui] pre,\nhtml[data-pixel-ui] code,\nhtml[data-pixel-ui] kbd,\nhtml[data-pixel-ui] textarea,\nhtml[data-pixel-ui] input,\nhtml[data-pixel-ui] p,\nhtml[data-pixel-ui] li,\nhtml[data-pixel-ui] td,\nhtml[data-pixel-ui] th {\n font-family: var(--px-mono) !important;\n}\nhtml[data-pixel-ui] p,\nhtml[data-pixel-ui] li {\n overflow-wrap: anywhere;\n}\n\n/* ── 按钮:默认只继承宿主的底与形(宿主大量图标按钮本来就是透明的),\n * 只把方角、像素字与「按下去掉一档」的反馈补齐;实心块只在悬停/选中时出现。\n * v1.4 给**所有** button 套 2px 黑框 + 内阴影,图标按钮全变成厚盒子,\n * 这是用户报「处处凹陷」的主因之一。 ─────────────────────────────── */\nhtml[data-pixel-ui] button {\n font-size: 12px;\n letter-spacing: 0;\n border: none !important;\n transition: background-color 0.06s steps(2), transform 0.06s steps(2);\n}\nhtml[data-pixel-ui] button:not(:disabled):hover {\n background-color: var(--px-wood-dark) !important;\n}\n/* 实心块按钮(工具栏/主按钮)才用整圈 2px bevel,列表行只用金色左条 */\nhtml[data-pixel-ui] button[class*='primary']:hover,\nhtml[data-pixel-ui] button[class*='Primary']:hover,\nhtml[data-pixel-ui] button[class*='danger']:hover,\n\nhtml[data-pixel-ui] button:not(:disabled):active {\n transform: translateY(1px);\n}\n/* 宿主已经画成「实心块」的按钮(主按钮/危险按钮)才补像素描边 */\nhtml[data-pixel-ui] button[class*='primary'],\nhtml[data-pixel-ui] button[class*='Primary'],\nhtml[data-pixel-ui] button[class*='danger'],\nhtml[data-pixel-ui] button[class*='Danger'] {\n border: 2px solid var(--px-line) !important;\n}\nhtml[data-pixel-ui] button:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n/* ── 页签(对话/轨迹/记忆):文字 + 金下沿,**不加边框**\n * v1.4 给页签套了 2px 黑框,整条读起来像凹陷的输入框(用户报「凹陷了」)。\n * 平面皮肤里「选中」只用颜色 + 一条整像素下边线表达,不做凹凸。 */\nhtml[data-pixel-ui] [role='tab'] {\n font-size: 13px;\n border: none !important;\n border-bottom: 3px solid transparent !important;\n background-color: transparent !important;\n color: var(--px-text-light) !important;\n}\nhtml[data-pixel-ui] [role='tab']:hover {\n color: var(--px-text-on-dark) !important;\n}\nhtml[data-pixel-ui] [role='tab'][aria-selected='true'] {\n color: var(--px-gold-bright) !important;\n background-color: transparent !important;\n border-bottom-color: var(--px-gold) !important;\n}\n\n/* ── 输入件:黑木底 + 羊皮纸字 + 金框,聚焦金环 ───────────────────\n * 只给「真正的输入面」上底和框,`select` 与只读输入保留宿主的形,\n * 否则设置页里成排的选择器全变成黑盒子。 */\nhtml[data-pixel-ui] input:not([type='checkbox']):not([type='radio']),\nhtml[data-pixel-ui] textarea,\nhtml[data-pixel-ui] [contenteditable='true'] {\n font-size: 12px;\n letter-spacing: 0;\n color: var(--px-input-text) !important;\n -webkit-text-fill-color: var(--px-input-text) !important;\n caret-color: var(--px-input-caret) !important;\n background-color: var(--px-input-bg) !important;\n border: 2px solid var(--px-gold-deep) !important;\n}\nhtml[data-pixel-ui] input:focus,\nhtml[data-pixel-ui] textarea:focus,\nhtml[data-pixel-ui] [contenteditable='true']:focus {\n border-color: var(--px-gold) !important;\n outline: none !important;\n}\nhtml[data-pixel-ui] select {\n color: var(--px-input-text) !important;\n background-color: transparent !important;\n border: 2px solid var(--px-gold-deep) !important;\n}\nhtml[data-pixel-ui] select:focus {\n border-color: var(--px-gold) !important;\n outline: none !important;\n}\nhtml[data-pixel-ui] input::placeholder,\nhtml[data-pixel-ui] textarea::placeholder {\n color: var(--px-input-ph) !important;\n -webkit-text-fill-color: var(--px-input-ph) !important;\n opacity: 1;\n}\nhtml[data-pixel-ui] [contenteditable='true']:empty::before {\n color: var(--px-input-ph) !important;\n -webkit-text-fill-color: var(--px-input-ph) !important;\n}\n\n/* ── 选择件:复选框 / 单选 / 开关 / 滑块 ───────────────────────── */\nhtml[data-pixel-ui] input[type='checkbox'],\nhtml[data-pixel-ui] input[type='radio'] {\n appearance: none;\n -webkit-appearance: none;\n width: 14px;\n height: 14px;\n padding: 0;\n flex-shrink: 0;\n background-color: var(--px-input-bg) !important;\n border: 2px solid var(--px-line) !important;\n}\nhtml[data-pixel-ui] input[type='checkbox']:checked {\n background-color: var(--px-gold) !important;\n /* 12×12 手绘像素对勾(2px 阶梯),定位取整像素 */\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' shape-rendering='crispEdges'%3E%3Cg fill='%231A0F06'%3E%3Crect x='0' y='4' width='2' height='2'/%3E%3Crect x='0' y='6' width='2' height='2'/%3E%3Crect x='2' y='6' width='2' height='2'/%3E%3Crect x='2' y='8' width='2' height='2'/%3E%3Crect x='4' y='4' width='2' height='2'/%3E%3Crect x='4' y='6' width='2' height='2'/%3E%3Crect x='4' y='8' width='2' height='2'/%3E%3Crect x='4' y='10' width='2' height='2'/%3E%3Crect x='6' y='2' width='2' height='2'/%3E%3Crect x='6' y='4' width='2' height='2'/%3E%3Crect x='6' y='6' width='2' height='2'/%3E%3Crect x='6' y='8' width='2' height='2'/%3E%3Crect x='8' y='0' width='2' height='2'/%3E%3Crect x='8' y='2' width='2' height='2'/%3E%3Crect x='8' y='4' width='2' height='2'/%3E%3Crect x='8' y='6' width='2' height='2'/%3E%3Crect x='10' y='0' width='2' height='2'/%3E%3Crect x='10' y='2' width='2' height='2'/%3E%3Crect x='10' y='4' width='2' height='2'/%3E%3C/g%3E%3C/svg%3E\") !important;\n background-repeat: no-repeat !important;\n background-position: 3px 1px !important;\n border: 2px solid var(--px-line) !important;\n}\nhtml[data-pixel-ui] input[type='radio']:checked {\n background-color: var(--px-gold) !important;\n /* 平色时代没有 inset 灰阶可用:8×8 手绘像素方点,整像素定位 */\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' shape-rendering='crispEdges'%3E%3Crect x='0' y='0' width='8' height='8' fill='%231A0F06'/%3E%3C/svg%3E\") !important;\n background-size: 6px 6px !important;\n background-position: 2px 2px !important;\n background-repeat: no-repeat !important;\n}\nhtml[data-pixel-ui] select {\n appearance: none;\n -webkit-appearance: none;\n padding-right: 28px !important;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8' shape-rendering='crispEdges'%3E%3Crect x='0' y='0' width='8' height='2'/%3E%3Crect x='1' y='2' width='6' height='2'/%3E%3Crect x='2' y='4' width='4' height='2'/%3E%3Crect x='3' y='6' width='2' height='2'/%3E%3C/svg%3E\") !important;\n background-repeat: no-repeat !important;\n background-position: right 8px center !important;\n}\nhtml[data-pixel-ui] select option {\n background-color: var(--px-input-bg);\n color: var(--px-input-text);\n}\nhtml[data-pixel-ui] [role='switch'] {\n border: 2px solid var(--px-line) !important;\n background-color: var(--px-wood-dark) !important;\n}\nhtml[data-pixel-ui] input[type='range'] {\n appearance: none;\n -webkit-appearance: none;\n height: 14px;\n padding: 0;\n border: none !important;\n background: transparent !important;\n}\nhtml[data-pixel-ui] input[type='range']::-webkit-slider-runnable-track {\n height: 8px;\n border: 2px solid var(--px-line);\n background-color: var(--px-wood-mid);\n}\nhtml[data-pixel-ui] input[type='range']::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 14px;\n margin-top: -5px;\n background-color: var(--px-gold);\n border: 2px solid var(--px-line);\n}\nhtml[data-pixel-ui] input[type='range']::-moz-range-track {\n height: 8px;\n border: 2px solid var(--px-line);\n background: var(--px-wood-mid);\n}\nhtml[data-pixel-ui] input[type='range']::-moz-range-thumb {\n width: 12px;\n height: 14px;\n background-color: var(--px-gold);\n border: 2px solid var(--px-line);\n border-radius: 0;\n}\n\n/* ── 内容层:代码块 / 行内代码 / 引用 / 表格 / 分隔线 ─────────────── */\nhtml[data-pixel-ui] pre {\n background: var(--px-code-bg) !important;\n color: var(--px-text-on-dark) !important;\n border: 2px solid var(--px-wood-light) !important;\n}\nhtml[data-pixel-ui] code {\n background: var(--px-code-bg) !important;\n color: var(--px-code-ink) !important;\n border: none !important;\n padding: 0 3px;\n}\nhtml[data-pixel-ui] pre code {\n background: transparent !important;\n color: inherit !important;\n padding: 0;\n}\nhtml[data-pixel-ui] blockquote {\n border-left: 4px solid var(--px-gold) !important;\n background-color: var(--px-wood-dark) !important;\n color: var(--px-text-on-dark) !important;\n padding: 6px 10px;\n}\nhtml[data-pixel-ui] hr {\n border: none !important;\n height: 2px;\n background: var(--px-wood-light) !important;\n}\nhtml[data-pixel-ui] table {\n border: 2px solid var(--px-wood-warm) !important;\n border-collapse: collapse;\n}\nhtml[data-pixel-ui] th {\n background-color: var(--px-wood-warm) !important;\n color: var(--px-ink-on-mid) !important;\n font-family: var(--px-font) !important;\n font-size: 12px;\n border: 1px solid var(--px-wood-light) !important;\n}\nhtml[data-pixel-ui] td {\n border: 1px solid var(--px-wood-light) !important;\n}\nhtml[data-pixel-ui] a {\n color: var(--px-gold-bright) !important;\n text-decoration-thickness: 2px;\n text-underline-offset: 3px;\n}\nhtml[data-pixel-ui] summary {\n cursor: pointer;\n color: var(--px-gold-bright) !important;\n}\n\n/* ── 工具提示 ───────────────────────────────────────────────────── */\nhtml[data-pixel-ui] [role='tooltip'] {\n font-size: 11px;\n line-height: 1.6;\n color: var(--px-parchment) !important;\n background: var(--px-tooltip-bg) !important;\n border: 2px solid var(--px-gold-deep) !important;\n}\nhtml[data-pixel-ui] [role='menuitem']:hover,\nhtml[data-pixel-ui] [role='option']:hover {\n background-color: var(--px-wood-mid) !important;\n}\n\n/* ── 像素光标(SVG data-URI 阶梯箭头) ──────────────────────────── */\nhtml[data-pixel-ui] body {\n cursor: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'%3E%3Cpath d='M2 2v17h3v-3h3v-3h3v-3h3v-3h3V2z' fill='%23FFFDF5' stroke='%231A0F06' stroke-width='2'/%3E%3C/svg%3E\") 2 2, auto;\n}\nhtml[data-pixel-ui] button,\nhtml[data-pixel-ui] a,\nhtml[data-pixel-ui] select,\nhtml[data-pixel-ui] [role='tab'],\nhtml[data-pixel-ui] [role='menuitem'],\nhtml[data-pixel-ui] [role='switch'] {\n cursor: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'%3E%3Cpath d='M2 2v17h3v-3h3v-3h3v-3h3v-3h3V2z' fill='%23F4D03F' stroke='%231A0F06' stroke-width='2'/%3E%3C/svg%3E\") 2 2, pointer;\n}\nhtml[data-pixel-ui] input,\nhtml[data-pixel-ui] textarea,\nhtml[data-pixel-ui] [contenteditable='true'] {\n cursor: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' shape-rendering='crispEdges'%3E%3Cpath d='M5 3h4v2h2v2h2v2h2v2h2v2h2v6h-2v2h-4v2H5z' fill='%23FFFDF5' stroke='%231A0F06' stroke-width='2'/%3E%3C/svg%3E\") 4 3, text;\n}\n\n/* ── 设置页「像素主题」行 ─────────────────────────────────────────\n * 这一段**不跟随皮肤作用域**:像素主题激活时用 --px-* 值,\n * 现代默认(浅色/深色/跟随系统)回落到 --dsw-alias-* 宿主 token,\n * 保证行在任何外观下都清晰可点、对比度达标。\n * 触摸目标 ≥32px(WCAG 2.2 web 目标 24px 之上留手感余量)。 */\n.px-theme-row {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 12px 0;\n border-top: 1px solid var(--px-wood-pale, var(--dsw-alias-border-l2, #6b5a3a));\n}\n.px-theme-row-title {\n font-family: var(--px-font, inherit);\n font-size: 12px;\n color: var(--px-gold-bright, var(--dsw-alias-label-primary));\n}\n.px-theme-row-desc {\n font-size: 11px;\n line-height: 1.6;\n color: var(--px-text-light, var(--dsw-alias-label-secondary));\n}\n.px-theme-row-cubes {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n.px-theme-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n min-height: 32px;\n padding: 6px 12px;\n font-family: var(--px-font, inherit);\n font-size: 11px;\n color: var(--px-text-on-dark, var(--dsw-alias-label-primary));\n background: var(--px-wood-dark, var(--dsw-alias-bg-layer-1));\n border: 2px solid var(--px-wood-darkest, var(--dsw-alias-border-l1));\n border-radius: 0 !important;\n cursor: pointer;\n transition: transform 0.06s steps(2), background-color 0.06s steps(2);\n}\n.px-theme-btn:hover {\n background: var(--px-wood-mid, var(--dsw-alias-bg-layer-2));\n}\n.px-theme-btn:active {\n transform: translateY(2px);\n}\n.px-theme-btn:focus-visible {\n outline: 2px solid var(--px-gold, var(--dsw-alias-state-business-primary, currentColor));\n outline-offset: 2px;\n}\n.px-theme-btn-active {\n color: var(--px-accent-ink, var(--dsw-alias-label-on-accent, #fff));\n background: var(--px-accent-bg, var(--dsw-alias-brand-primary));\n border-color: var(--px-gold-deep, var(--dsw-alias-border-l2));\n}\n.px-theme-swatch {\n width: 10px;\n height: 10px;\n border: 2px solid #000;\n flex-shrink: 0;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["h", "host"]
7
7
  }
package/lib/index.js CHANGED
@@ -2,15 +2,21 @@
2
2
  * dsh-pixel-ui — node half.
3
3
  *
4
4
  * Serves the bundled pixel fonts to the browser half under
5
- * `/dsh-pixel-ui/fonts/<file>` (filename whitelist — no path traversal).
6
- * The theme itself lives entirely in the browser half; this package is
7
- * dual-face only so the fonts have a stable same-origin URL.
5
+ * `/dsh-pixel-ui/fonts/<file>` (filename whitelist — no path traversal) and
6
+ * persists the selected theme under `$DSH_HOME/storages/dsh-pixel-ui/preference.json`
7
+ * behind `/dsh-pixel-ui/preference` (GET reads, PUT writes).
8
+ *
9
+ * The durable preference is what fixes DSH Desktop's random local port: the
10
+ * browser half keys `localStorage` per origin (including the port), so a
11
+ * per-launch port forgot the chosen skin on every restart. The host file is
12
+ * port-independent, and the browser half prefers it over its local mirror.
8
13
  *
9
14
  * @module dsh-pixel-ui
10
15
  */
11
- import { readFileSync } from 'node:fs'
16
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'
12
17
  import { join } from 'node:path'
13
18
  import { fileURLToPath } from 'node:url'
19
+ import { resolveDshHome } from '@deepseek-ai/dsh-home-paths'
14
20
 
15
21
  /** Plugin name; also the patch row id. */
16
22
  export const name = 'dsh-pixel-ui'
@@ -25,8 +31,85 @@ const FONT_TYPES = {
25
31
  'PressStart2P-Regular.ttf': 'font/ttf',
26
32
  }
27
33
 
34
+ /** Built-in preference ids plus this plugin's own pixel ids. */
35
+ const BUILTIN_IDS = new Set(['light', 'dark', 'system'])
36
+
37
+ /** Theme ids accepted from the wire: built-ins or `pixel-*`. */
38
+ function normalizeTheme(value) {
39
+ if (typeof value !== 'string') return null
40
+ const id = value.trim()
41
+ if (BUILTIN_IDS.has(id) || /^pixel-[a-z0-9-]{1,48}$/.test(id)) return id
42
+ return null
43
+ }
44
+
45
+ const PREFERENCE_DIR = join(resolveDshHome(), 'storages', 'dsh-pixel-ui')
46
+ const PREFERENCE_FILE = join(PREFERENCE_DIR, 'preference.json')
47
+
28
48
  /**
29
- * Mount the font route.
49
+ * Page-bootstrap global carrying the durable preference.
50
+ *
51
+ * DSH Desktop renders the page from `dsh-app://app/`, so a same-origin
52
+ * relative fetch of the preference route (the Web deployment's transport)
53
+ * cannot work there. The Host knows the value before the first paint, so the
54
+ * index injection is the one transport that exists in both deployments.
55
+ */
56
+ const BOOTSTRAP_GLOBAL = '__DSH_PIXEL_UI__'
57
+
58
+ /** Read the durable preference; missing or corrupt files read as unset. */
59
+ function readPreference() {
60
+ try {
61
+ const parsed = JSON.parse(readFileSync(PREFERENCE_FILE, 'utf8'))
62
+ return normalizeTheme(parsed?.theme)
63
+ } catch {
64
+ return null
65
+ }
66
+ }
67
+
68
+ /** Atomically persist the preference (tmp + rename). Storage failures are non-fatal. */
69
+ function writePreference(theme) {
70
+ try {
71
+ mkdirSync(PREFERENCE_DIR, { recursive: true })
72
+ const tmp = `${PREFERENCE_FILE}.${process.pid}.tmp`
73
+ writeFileSync(tmp, `${JSON.stringify({ theme })}\n`)
74
+ renameSync(tmp, PREFERENCE_FILE)
75
+ return true
76
+ } catch {
77
+ return false
78
+ }
79
+ }
80
+
81
+ /** Collect a JSON request body with a hard size cap. */
82
+ function readJsonBody(req, limitBytes = 2048) {
83
+ return new Promise((resolve, reject) => {
84
+ const chunks = []
85
+ let size = 0
86
+ req.on('data', (chunk) => {
87
+ size += chunk.length
88
+ if (size > limitBytes) {
89
+ reject(new Error('body too large'))
90
+ req.destroy()
91
+ return
92
+ }
93
+ chunks.push(chunk)
94
+ })
95
+ req.on('end', () => {
96
+ try { resolve(JSON.parse(Buffer.concat(chunks).toString('utf8'))) }
97
+ catch (error) { reject(error) }
98
+ })
99
+ req.on('error', reject)
100
+ })
101
+ }
102
+
103
+ const json = (res, status, payload) => {
104
+ res.writeHead(status, {
105
+ 'content-type': 'application/json; charset=utf-8',
106
+ 'cache-control': 'no-store',
107
+ })
108
+ res.end(JSON.stringify(payload))
109
+ }
110
+
111
+ /**
112
+ * Mount the plugin routes: the font prefix and the preference endpoint.
30
113
  * @param ctx - context carrying webServer.
31
114
  */
32
115
  export function apply(ctx) {
@@ -49,4 +132,36 @@ export function apply(ctx) {
49
132
  res.end(readFileSync(join(FONTS_DIR, file)))
50
133
  },
51
134
  }), 'dsh-pixel-ui: fonts route')
135
+
136
+ ctx.effect(() => ctx.webServer.register({
137
+ kind: 'exact',
138
+ path: '/dsh-pixel-ui/preference',
139
+ handler: async (req, res) => {
140
+ if (req.method === 'GET' || req.method === 'HEAD') {
141
+ json(res, 200, { theme: readPreference() })
142
+ return
143
+ }
144
+ if (req.method === 'PUT' || req.method === 'POST') {
145
+ let theme = null
146
+ try {
147
+ const body = await readJsonBody(req)
148
+ theme = normalizeTheme(body?.theme)
149
+ } catch {
150
+ json(res, 400, { error: 'invalid json body' })
151
+ return
152
+ }
153
+ if (theme === null || !writePreference(theme)) {
154
+ json(res, 400, { error: 'theme must be light, dark, system or pixel-*' })
155
+ return
156
+ }
157
+ json(res, 200, { theme })
158
+ return
159
+ }
160
+ json(res, 405, { error: 'method not allowed' })
161
+ },
162
+ }), 'dsh-pixel-ui: preference route')
163
+
164
+ ctx.effect(() => ctx.on('webserver/index-inject', (table) => {
165
+ table.push({ kind: 'global', name: BOOTSTRAP_GLOBAL, value: { theme: readPreference() } })
166
+ }), 'dsh-pixel-ui: boot preference injection')
52
167
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-pixel-ui",
3
- "version": "1.0.0",
4
- "description": "DSH 像素皮肤:Agent Xi 风格四主题(木屋/羊皮纸/暖阳/终端绿)+ fusion-pixel/Press Start 2P 像素字体 + CRT 扫描线,可一键切回现代默认",
3
+ "version": "1.5.1",
4
+ "description": "DSH 像素皮肤:Agent Xi 风格四主题(木屋/羊皮纸/暖阳/终端绿)+ fusion-pixel/Press Start 2P 像素字体;完全平面(无立体/无阴影)、整像素描边、内容列宽随视口撑开;宿主侧记忆主题选择,桌面端 dsh-app:// 也能恢复",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "files": [
@@ -17,7 +17,29 @@
17
17
  "./package.json": "./package.json"
18
18
  },
19
19
  "scripts": {
20
- "build": "node scripts/build.mjs"
20
+ "build": "node scripts/build.mjs",
21
+ "test": "node scripts/check-compat.mjs && node scripts/test-host.mjs && node scripts/test-client.mjs && node scripts/audit-contrast.mjs",
22
+ "test:compat": "node scripts/check-compat.mjs",
23
+ "test:host": "node scripts/test-host.mjs",
24
+ "test:client": "node scripts/test-client.mjs",
25
+ "test:contrast": "node scripts/audit-contrast.mjs"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/zhang66633/dsh-pixel-ui.git"
30
+ },
31
+ "keywords": [
32
+ "dsh",
33
+ "dsh-plugin",
34
+ "deepseek-harness",
35
+ "theme",
36
+ "skin",
37
+ "pixel",
38
+ "retro",
39
+ "ui"
40
+ ],
41
+ "engines": {
42
+ "dsh": ">=0.1.0-rc.6 <0.2.0-0 || >=0.2.0-rc.1 <0.3.0-0 || >=0.2.1-0 <0.3.0-0"
21
43
  },
22
44
  "dsh": {
23
45
  "bundle": {
@@ -30,13 +52,30 @@
30
52
  "@deepseek-ai/dsh-client-ui-settings-general"
31
53
  ],
32
54
  "platform": "web"
55
+ },
56
+ "compatibility": {
57
+ "dsh": ">=0.1.0-rc.6 <0.3.0-0",
58
+ "dshReleases": {
59
+ "0.1.0-rc.6": "compatible",
60
+ "0.1.0-rc.7": "compatible",
61
+ "0.1.7-rc.2": "compatible",
62
+ "0.2.0-rc.1": "compatible",
63
+ "0.2.0-rc.2": "compatible",
64
+ "0.2.1-rc.3": "compatible"
65
+ }
33
66
  }
34
67
  },
35
68
  "peerDependencies": {
36
- "@deepseek-ai/cordis": "^4.0.1"
69
+ "@deepseek-ai/cordis": "^4.0.1",
70
+ "@deepseek-ai/dsh-client-store": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.3-0 || ^0.1.5-0 || >=0.2.0-rc.1 <0.3.0-0 || >=0.2.1-0 <0.3.0-0",
71
+ "@deepseek-ai/dsh-client-ui-theme": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.3-0 || ^0.1.5-0 || >=0.2.0-rc.1 <0.3.0-0 || >=0.2.1-0 <0.3.0-0",
72
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.3-0 || ^0.1.5-0 || >=0.2.0-rc.1 <0.3.0-0 || >=0.2.1-0 <0.3.0-0",
73
+ "@deepseek-ai/dsh-client-ui-settings-general": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.3-0 || ^0.1.5-0 || >=0.2.0-rc.1 <0.3.0-0 || >=0.2.1-0 <0.3.0-0",
74
+ "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6 || ^0.1.1-0 || ^0.1.2-0 || ^0.1.3-0 || ^0.1.5-0 || >=0.2.0-rc.1 <0.3.0-0 || >=0.2.1-0 <0.3.0-0"
37
75
  },
38
76
  "devDependencies": {
39
- "esbuild": "^0.28.2"
77
+ "esbuild": "^0.28.2",
78
+ "semver": "^7.8.5"
40
79
  },
41
80
  "license": "MIT"
42
81
  }
Binary file
Binary file
Binary file
Binary file