@jayyuen66/dsh-plugin-shared 0.2.5 → 0.2.7

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/README.md CHANGED
@@ -136,7 +136,7 @@ The subpath column is the in-package specifier; value imports prefix it with the
136
136
  | Subpath | Provides |
137
137
  | --- | --- |
138
138
  | `.` | Barrel over the runtime modules below, served from `dist/plugin.js`: the same names plus `name` and a no-op `apply`, so the host loader can also take this package as a row. `canonicalize-region-paths` (build-time), `remote-marker` (value-imports typert), and `card-ui` (client-only) deliberately stay out; the root barrel must not pull an unused package into every consumer. |
139
- | `./lib/tool-events` | `scanToolEvents` returns the `calls` / `results` tables, PTC sub-calls included; `toolEventRowsOf` handles a single event and already carries normalized `arguments` and `badArguments`. `editPathOf` returns a `kind`-discriminated `EditTarget` with only `write` / `read-view`; the path is `undefined` when it cannot be read. `applyPatchTargetsOf` parses the ordered operations of an `apply_patch` input with the official `dsh-fs-patch`; `editPathsOf` expands every write target, including both sides of a move. Record fields are `readonly`. Re-exports the official `SessionEvent`; `PtcDispatchStartEventData` / `PtcDispatchEventData` are declared in this package and augment `SessionEventMap` locally, because the host moved that augmentation between packages inside the supported window. |
139
+ | `./lib/tool-events` | `scanToolEvents` returns the `calls` / `results` tables, PTC sub-calls included; `toolEventRowsOf` handles a single event and already carries normalized `arguments` and `badArguments`. `editPathOf` returns a `kind`-discriminated `EditTarget` with only `write` / `read-view`; the path is `undefined` when it cannot be read. `applyPatchTargetsOf` parses the ordered operations of an `apply_patch` input with the official `dsh-fs-patch`; `editPathsOf` expands every write target, including both sides of a move. Record fields are `readonly`. Each result row also carries the structured failure identity `error` from the event's `data.error` (`name` and `code` must be non-empty strings; a partial shape reads as absent, and `reason` is kept only when it is a string). Re-exports the official `SessionEvent`; `PtcDispatchStartEventData` / `PtcDispatchEventData` are declared in this package and augment `SessionEventMap` locally, because the host moved that augmentation between packages inside the supported window. |
140
140
  | `./lib/card-apply` | `claimApply` + `CardApplyCtx`: an apply idempotency guard built on an **own** flag on `globalThis` (a symbol, or `true` written by an outside holder) with `ctx.effect` cleanup. The flag is process-wide, so a second context on the same page is rejected by design. |
141
141
  | `./lib/card-diff` | `diffTouched`: which touched fields really differ from the snapshot, compared by form value. `undefined`, `null` and an absent key are one value; arrays and objects compare element by element; cycles terminate. |
142
142
  | `./lib/card-ui` | Client-only settings-card kit: `CardForm`, `Section`, `FieldGrid`, `StatusBar`, `Field`, `FieldLabel`, `InfoTip`, `ToggleField`, `NumberField`, `TextField`, `ListField`, `SelectField`, `SaveBar`, `splitHint` and the `CARD_UI_CSS` string. Built with `createElement` only; `react` is an optional peer and the subpath never enters the root barrel. The declared peer floor is `^19.3.0`; the kit uses only APIs that already existed in React 18 (`useId`, `useState`, `useEffect`, refs). |
package/README.zh-CN.md CHANGED
@@ -136,7 +136,7 @@ import { isRecord, fieldOf } from "@jayyuen66/dsh-plugin-shared/lib/record";
136
136
  | 子路径 | 提供什么 |
137
137
  | --- | --- |
138
138
  | `.` | 下面列出的运行期模块的 barrel,由 `dist/plugin.js` 提供:同样的导出名,再加 `name` 与一条空操作的 `apply`,宿主 Loader 因此也能把本包当作一行载入。`canonicalize-region-paths`(构建期)、`remote-marker`(值导入 typert)与 `card-ui`(客户端专用)刻意不进;根 barrel 不得把用不到的包带进每个消费方 |
139
- | `./lib/tool-events` | `scanToolEvents` 返回 `calls` / `results` 两张表,PTC 子调用一并合并;`toolEventRowsOf` 处理单个事件,交回的记录已带归一后的 `arguments` 与 `badArguments`。`editPathOf` 返回按 `kind` 判别的 `EditTarget`,只有 `write` / `read-view` 两个分支;路径取不到时为 `undefined`。`applyPatchTargetsOf` 用官方 `dsh-fs-patch` 解析 `apply_patch` 的有序操作;`editPathsOf` 展开全部写目标(move 的源与目的都算)。记录字段一律 `readonly`。同时再导出官方 `SessionEvent`;`PtcDispatchStartEventData` / `PtcDispatchEventData` 在本包内声明并自行增强 `SessionEventMap`,因为宿主在支持窗口内换过承载这份增强的包 |
139
+ | `./lib/tool-events` | `scanToolEvents` 返回 `calls` / `results` 两张表,PTC 子调用一并合并;`toolEventRowsOf` 处理单个事件,交回的记录已带归一后的 `arguments` 与 `badArguments`。`editPathOf` 返回按 `kind` 判别的 `EditTarget`,只有 `write` / `read-view` 两个分支;路径取不到时为 `undefined`。`applyPatchTargetsOf` 用官方 `dsh-fs-patch` 解析 `apply_patch` 的有序操作;`editPathsOf` 展开全部写目标(move 的源与目的都算)。记录字段一律 `readonly`。结果行另带结算事件 `data.error` 的结构化失败身份 `error`(`name` / `code` 必须是非空字符串,半截形状按缺席处理;`reason` 只在它是字符串时保留)。同时再导出官方 `SessionEvent`;`PtcDispatchStartEventData` / `PtcDispatchEventData` 在本包内声明并自行增强 `SessionEventMap`,因为宿主在支持窗口内换过承载这份增强的包 |
140
140
  | `./lib/card-apply` | `claimApply` + `CardApplyCtx`:保证 apply 幂等的保护逻辑,建立在 `globalThis` 上的**自有**标记(symbol,或外部写入的 `true`)加 `ctx.effect` 清理。标记是进程级的,所以同一页面的第二个 ctx 会被拒绝——这是有意的设计 |
141
141
  | `./lib/card-diff` | `diffTouched`:touched 层里哪些字段真的与快照不同,按表单值比较。`undefined`、`null` 与键缺失是同一种值;数组与对象逐元素比较;循环引用也能正常结束 |
142
142
  | `./lib/card-ui` | 客户端专用设置卡组件:`CardForm`、`Section`、`FieldGrid`、`StatusBar`、`Field`、`FieldLabel`、`InfoTip`、`ToggleField`、`NumberField`、`TextField`、`ListField`、`SelectField`、`SaveBar`、`splitHint` 与样式字符串 `CARD_UI_CSS`。只用 `createElement`;`react` 是 optional peer,该子路径永不进根 barrel。声明的 peer 下限是 `^19.3.0`;组件只用 React 18 就已存在的 API(`useId`、`useState`、`useEffect`、refs) |
@@ -18,11 +18,11 @@ import{createElement as e,useCallback as t,useEffect as n,useId as r,useLayoutEf
18
18
  .dsu-helpBtn{appearance:none;border:0;background:transparent;padding:0;width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary);cursor:help;border-radius:50%;flex:none}
19
19
  .dsu-help:hover .dsu-helpBtn,.dsu-helpBtn:focus-visible{color:var(--dsw-alias-label-primary)}
20
20
  .dsu-helpBtn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
21
- .dsu-tip{position:fixed;z-index:60;box-sizing:border-box;max-width:min(340px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:auto;overflow-wrap:anywhere;padding:10px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);box-shadow:0 12px 32px rgb(0 0 0 / 45%);font-size:12px;line-height:1.6;font-weight:400;color:var(--dsw-alias-label-secondary)}
21
+ .dsu-tip{width:max-content;position:fixed;position-area:block-end span-inline-end;justify-self:start;margin-top:8px;z-index:60;box-sizing:border-box;max-width:min(340px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:auto;overflow-wrap:anywhere;padding:10px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);box-shadow:0 12px 32px rgb(0 0 0 / 45%);font-size:12px;line-height:1.6;font-weight:400;color:var(--dsw-alias-label-secondary)}
22
22
  .dsu-input{width:100%;height:34px;padding:0 12px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}
23
23
  .dsu-input:focus-visible{outline:none;border-color:var(--dsw-alias-state-business-primary)}
24
24
  .dsu-input[aria-invalid="true"]{border-color:var(--dsw-alias-state-error-primary)}
25
- .dsu-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
25
+ .dsu-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed}
26
26
  .dsu-input::placeholder{color:var(--dsw-alias-label-tertiary)}
27
27
  .dsu-textarea{height:auto;min-height:96px;padding:8px 12px;line-height:1.6;resize:vertical}
28
28
  .dsu-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
@@ -43,7 +43,7 @@ import{createElement as e,useCallback as t,useEffect as n,useId as r,useLayoutEf
43
43
  .dsu-selectValue{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
44
44
  .dsu-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s}
45
45
  .dsu-select[aria-expanded="true"] .dsu-chevron{transform:rotate(180deg)}
46
- .dsu-menu{position:fixed;position-area:block-end span-inline-start;justify-self:start;margin-top:6px;z-index:70;padding:4px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);border-radius:12px;box-shadow:0 12px 32px rgb(0 0 0 / 50%);max-height:280px;overflow:auto}
46
+ .dsu-menu{position:fixed;position-area:block-end span-inline-end;justify-self:start;margin-top:6px;z-index:70;padding:4px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);border-radius:12px;box-shadow:0 12px 32px rgb(0 0 0 / 50%);max-height:280px;overflow:auto}
47
47
  .dsu-option{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:1.4;width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 10px;border-radius:8px;cursor:pointer;text-align:left}
48
48
  .dsu-option:hover,.dsu-option[data-active="true"]{background:var(--dsw-alias-bg-layer-3)}
49
49
  .dsu-option:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed}
@@ -76,30 +76,28 @@ import{createElement as e,useCallback as t,useEffect as n,useId as r,useLayoutEf
76
76
  @media (prefers-reduced-motion:reduce){
77
77
  .dsu-switch,.dsu-knob,.dsu-chevron,.dsu-btn{transition:none}
78
78
  }
79
- `;function l(e,t=30){return e.length<=t?{hint:e}:{detail:e}}function u(t){return e(`div`,{className:`dsu-form`},t.children)}function d(t){return e(`section`,{className:`dsu-section`},e(`h2`,{className:`dsu-sectionTitle`},t.title),t.hint===void 0?null:e(`p`,{className:`dsu-sectionHint`},t.hint),t.children)}function f(t){return e(`div`,{className:`dsu-grid`},t.children)}function p(t){if(t.items.length===0)return null;let n=t.items.map((t,n)=>e(`span`,{key:n},n===0?t:`· ${t}`));return e(`div`,{className:`dsu-statusbar`},...n)}function m(a){let c=r(),[l,u]=s(!1),[d,f]=s(!1),p=o(!1),m=o(!1),h=o(!1),g=o(null),_=o(null),v=o(null),y=t(()=>{v.current!==null&&(clearTimeout(v.current),v.current=null)},[]),b=t(()=>{let e=g.current,t=_.current;
80
- /* v8 ignore next 3 -- place 只在 open 的 layout effect 与挂载期监听器里调用;refs 为空只可能是卸载竞态 */
81
- if(e===null||t===null)return;let n=e.getBoundingClientRect(),r=t.offsetWidth,i=t.offsetHeight,{left:a}=n,o=n.bottom+8;o+i>window.innerHeight-8&&(o=n.top-i-8),a+r>window.innerWidth-8&&(a=window.innerWidth-r-8);let s=Math.max(8,window.innerWidth-r-8),c=Math.max(8,window.innerHeight-i-8);t.style.left=`${Math.min(Math.max(8,a),s)}px`,t.style.top=`${Math.min(Math.max(8,o),c)}px`},[]),x=t(()=>{y(),h.current=!1,f(!1),u(!1)},[y]),S=t(()=>{y(),u(!0)},[y]),C=t(()=>{y(),p.current||m.current||h.current||(v.current=setTimeout(()=>{
79
+ `;function l(e,t=30){return e.length<=t?{hint:e}:{detail:e}}function u(t){return e(`div`,{className:`dsu-form`},t.children)}function d(t){return e(`section`,{className:`dsu-section`},e(`h2`,{className:`dsu-sectionTitle`},t.title),t.hint===void 0?null:e(`p`,{className:`dsu-sectionHint`},t.hint),t.children)}function f(t){return e(`div`,{className:`dsu-grid`},t.children)}function p(t){if(t.items.length===0)return null;let n=t.items.map((t,n)=>e(`span`,{key:n},n===0?t:`· ${t}`));return e(`div`,{className:`dsu-statusbar`},...n)}function m(){return`happyDOM`in globalThis?!1:typeof CSS<`u`&&typeof CSS.supports==`function`&&CSS.supports(`position-area: block-end`)}function h(a){let c=r(),l=`--dsu-tip-${c.replaceAll(/[^a-zA-Z0-9]/gu,``)}`,[u,d]=s(!1),f=o(!1),p=o(!1),h=o(!1),g=o(null),_=o(null),v=o(null),y=o(null),b=t(()=>{y.current!==null&&(clearTimeout(y.current),y.current=null)},[]),x=t(()=>{let e=_.current,t=v.current;if(e===null||t===null||m())return;t.style.marginTop=`0px`;let n=e.getBoundingClientRect(),r=t.offsetWidth,i=t.offsetHeight,{left:a}=n,o=n.bottom+8;o+i>window.innerHeight-8&&(o=n.top-i-8),a+r>window.innerWidth-8&&(a=window.innerWidth-r-8);let s=Math.max(8,window.innerWidth-r-8),c=Math.max(8,window.innerHeight-i-8);t.style.left=`${Math.min(Math.max(8,a),s)}px`,t.style.top=`${Math.min(Math.max(8,o),c)}px`},[]),S=t(()=>{b(),h.current=!1,d(!1)},[b]),C=t(()=>{b(),d(!0)},[b]),w=t(()=>{b(),f.current||p.current||h.current||(y.current=setTimeout(()=>{
82
80
  /* v8 ignore next 3 -- 任何重新进入/钉住都会 cancelHide;这次复查只防未来的异步路径 */
83
- v.current=null,p.current||m.current||h.current||u(!1)},120))},[y]),w=t(e=>{h.current=e,f(e)},[]);i(()=>{if(!l)return()=>{};b();let e=e=>{let{target:t}=e;t instanceof Node&&_.current?.contains(t)===!0||(h.current?b():x())},t=()=>{b()};return window.addEventListener(`scroll`,e,!0),window.addEventListener(`resize`,t),()=>{window.removeEventListener(`scroll`,e,!0),window.removeEventListener(`resize`,t)}},[l,a.detail,b,x]),n(()=>{if(!l)return()=>{};let e=e=>{e.key===`Escape`&&x()};return document.addEventListener(`keydown`,e),()=>{document.removeEventListener(`keydown`,e)}},[l,x]),n(()=>y,[y]);let T=e(`button`,{ref:g,type:`button`,className:`dsu-helpBtn`,"aria-label":`${a.label} 说明`,"aria-describedby":c,"aria-pressed":d,onClick:()=>{h.current?x():(w(!0),S())}},e(`svg`,{width:12,height:12,viewBox:`0 0 14 14`,"aria-hidden":!0},e(`circle`,{cx:7,cy:7,r:6,fill:`none`,stroke:`currentColor`,strokeWidth:1.2}),e(`path`,{d:`M7 6.2v4`,stroke:`currentColor`,strokeWidth:1.3,strokeLinecap:`round`}),e(`circle`,{cx:7,cy:4.3,r:.8,fill:`currentColor`}))),E=e(`div`,{ref:_,id:c,role:`tooltip`,className:`dsu-tip`,hidden:!l},a.detail),D=a.showLabel===!1?null:e(`span`,{className:`dsu-label${a.compact===!0?` dsu-labelMono`:``}`},a.label);return e(`span`,{className:`dsu-help`,onMouseEnter:()=>{p.current=!0,S()},onMouseLeave:()=>{p.current=!1,C()},onFocus:()=>{m.current=!0,S()},onBlur:e=>{let t=e.relatedTarget;t instanceof Node&&e.currentTarget.contains(t)||(m.current=!1,C())}},D,T,E)}function h(t){let n=`dsu-label${t.compact===!0?` dsu-labelMono`:``}`,r=t.controlId===void 0?e(`span`,{className:n},t.label):e(`label`,{className:n,htmlFor:t.controlId},t.label);return e(`div`,{className:`dsu-labelRow`},r,t.detail===void 0?null:e(m,{label:t.label,detail:t.detail,showLabel:!1}))}function g(t){let n=t.controlId===void 0?void 0:`${t.controlId}-hint`;return e(`div`,{className:`dsu-field${t.wide===!0?` dsu-fieldWide`:``}`},e(`div`,{className:`dsu-fieldMeta`},e(h,{label:t.label,detail:t.detail,compact:t.compact,controlId:t.controlId}),t.hint===void 0?null:e(`p`,{className:`dsu-hint`,id:n},t.hint)),e(`div`,{className:`dsu-control`},t.children))}function _(t){let n=r(),i=t.hint===void 0?void 0:`${n}-hint`;return e(g,{...t,controlId:n},e(`button`,{type:`button`,role:`switch`,id:n,className:`dsu-switch`,"aria-checked":t.checked,"aria-describedby":i,"data-field":t.field,disabled:t.disabled===!0,onClick:()=>{t.onChange(!t.checked)}},e(`span`,{className:`dsu-knob`})))}function v(t){let i=typeof t.value==`number`&&Number.isFinite(t.value)?String(t.value):``,[a,c]=s(i),[l,u]=s(!1),d=o(void 0),f=r(),p=r(),m=[t.hint===void 0?void 0:`${f}-hint`,l?p:void 0].filter(e=>e!==void 0),h=m.length===0?void 0:m.join(` `);n(()=>{t.value!==d.current&&(d.current=t.value,c(i),u(!1))},[t.value,i]);let _=e=>{let n=t.min??-1/0,r=t.max??1/0;if(!Number.isFinite(e)||e<n||e>r)return!1;let{step:i}=t;if(i===void 0||i<=0)return!0;let a=(e-(t.min??0))/i;return Math.abs(a-Math.round(a))<1e-9},v=e=>{if(c(e),e===``){u(!1),d.current=void 0,t.onChange(void 0);return}let n=Number(e);_(n)?(u(!1),d.current=n,t.onChange(n)):u(!0)},y=()=>{a===``||l||c(String(Number(a)))};return e(g,{...t,controlId:f},e(`div`,{className:`dsu-numberWrap`},e(`div`,{className:`dsu-number`},e(`input`,{className:`dsu-input`,type:`number`,inputMode:`numeric`,id:f,value:a,min:t.min,max:t.max,step:t.step,placeholder:t.placeholder,"aria-invalid":l||void 0,"aria-describedby":h,"data-field":t.field,disabled:t.disabled===!0,onChange:e=>{v(e.target.value)},onBlur:y}),t.unit===void 0?null:e(`span`,{className:`dsu-unit`},t.unit)),l?e(`p`,{className:`dsu-error`,id:p},t.invalidHint):null))}function y(t){let[i,a]=s(t.value),[o,c]=s(!1),l=r(),u=t.hint===void 0?void 0:`${l}-hint`;n(()=>{o||a(t.value)},[t.value,o]);let d={id:l,className:`dsu-input${t.multiline===!0?` dsu-textarea`:``}${t.monospace===!0?` dsu-mono`:``}`,value:i,placeholder:t.placeholder,"aria-describedby":u,"data-field":t.field,disabled:t.disabled===!0,onFocus:()=>{c(!0)},onBlur:()=>{c(!1)},onChange:e=>{a(e.target.value),t.onChange(e.target.value)}};return e(g,{...t,controlId:l},t.multiline===!0?e(`textarea`,d):e(`input`,d))}function b(t){let i=t.separator??`comma`,a=t.value.join(i===`newline`?`
81
+ y.current=null,f.current||p.current||h.current||d(!1)},120))},[b]),T=t(e=>{h.current=e},[]);i(()=>{if(!u)return()=>{};x();let e=e=>{let{target:t}=e;t instanceof Node&&v.current?.contains(t)===!0||(h.current?x():S())},t=()=>{x()},n=e=>{let t=e.target;g.current?.contains(t)!==!0&&S()};return document.addEventListener(`pointerdown`,n,!0),window.addEventListener(`scroll`,e,!0),window.addEventListener(`resize`,t),()=>{document.removeEventListener(`pointerdown`,n,!0),window.removeEventListener(`scroll`,e,!0),window.removeEventListener(`resize`,t)}},[u,a.detail,x,S]),n(()=>{if(!u)return()=>{};let e=e=>{e.key===`Escape`&&S()};return document.addEventListener(`keydown`,e),()=>{document.removeEventListener(`keydown`,e)}},[u,S]),n(()=>b,[b]);let E=e(`button`,{ref:_,type:`button`,className:`dsu-helpBtn`,style:{anchorName:l},"aria-label":`${a.label} 说明`,"aria-expanded":u,"aria-controls":c,onClick:()=>{h.current?S():(T(!0),C())}},e(`svg`,{width:12,height:12,viewBox:`0 0 14 14`,"aria-hidden":!0},e(`circle`,{cx:7,cy:7,r:6,fill:`none`,stroke:`currentColor`,strokeWidth:1.2}),e(`path`,{d:`M7 6.2v4`,stroke:`currentColor`,strokeWidth:1.3,strokeLinecap:`round`}),e(`circle`,{cx:7,cy:4.3,r:.8,fill:`currentColor`}))),D=e(`div`,{ref:v,id:c,role:`tooltip`,className:`dsu-tip`,style:{positionAnchor:l,positionTryFallbacks:`flip-block, flip-inline`},hidden:!u},a.detail),O=a.showLabel===!1?null:e(`span`,{className:`dsu-label${a.compact===!0?` dsu-labelMono`:``}`},a.label);return e(`span`,{ref:g,className:`dsu-help`,onMouseEnter:()=>{f.current=!0,C()},onMouseLeave:()=>{f.current=!1,w()},onFocus:()=>{p.current=!0,C()},onBlur:e=>{let t=e.relatedTarget;t instanceof Node&&e.currentTarget.contains(t)||(p.current=!1,w())}},O,E,D)}function g(t){let n=`dsu-label${t.compact===!0?` dsu-labelMono`:``}`,r=t.controlId===void 0?e(`span`,{className:n},t.label):e(`label`,{className:n,htmlFor:t.controlId},t.label);return e(`div`,{className:`dsu-labelRow`},r,t.detail===void 0?null:e(h,{label:t.label,detail:t.detail,showLabel:!1}))}function _(t){let n=t.controlId===void 0?void 0:`${t.controlId}-hint`;return e(`div`,{className:`dsu-field${t.wide===!0?` dsu-fieldWide`:``}`},e(`div`,{className:`dsu-fieldMeta`},e(g,{label:t.label,detail:t.detail,compact:t.compact,controlId:t.controlId}),t.hint===void 0?null:e(`p`,{className:`dsu-hint`,id:n},t.hint)),e(`div`,{className:`dsu-control`},t.children))}function v(t){let n=r(),i=t.hint===void 0?void 0:`${n}-hint`;return e(_,{...t,controlId:n},e(`button`,{type:`button`,role:`switch`,id:n,className:`dsu-switch`,"aria-checked":t.checked,"aria-describedby":i,"data-field":t.field,disabled:t.disabled===!0,onClick:()=>{t.onChange(!t.checked)}},e(`span`,{className:`dsu-knob`})))}function y(t){let i=typeof t.value==`number`&&Number.isFinite(t.value)?String(t.value):``,[a,c]=s(i),[l,u]=s(!1),[d,f]=s(!1),p=o(void 0),m=r(),h=r(),g=t.hint===void 0?void 0:`${m}-hint`,v=l&&d,y=[g,v?h:void 0].filter(e=>e!==void 0),b=y.length===0?void 0:y.join(` `);n(()=>{t.value!==p.current&&(p.current=t.value,c(i),u(!1),f(!1))},[t.value,i]);let x=e=>{let n=t.min??-1/0,r=t.max??1/0;if(!Number.isFinite(e)||e<n||e>r)return!1;let{step:i}=t;if(i===void 0||i<=0)return!0;let a=(e-(t.min??0))/i;return Math.abs(a-Math.round(a))<1e-9},S=e=>{if(f(!1),c(e),e===``){u(!1),p.current=void 0,t.onChange(void 0);return}let n=Number(e);x(n)?(u(!1),p.current=n,t.onChange(n)):u(!0)},C=()=>{a===``||l||c(String(Number(a)))},w=()=>{f(l),C()};return e(_,{...t,controlId:m},e(`div`,{className:`dsu-numberWrap`},e(`div`,{className:`dsu-number`},e(`input`,{className:`dsu-input`,type:`number`,inputMode:`numeric`,id:m,value:a,min:t.min,max:t.max,step:t.step,placeholder:t.placeholder,"aria-invalid":v||void 0,"aria-describedby":b,"data-field":t.field,disabled:t.disabled===!0,onChange:e=>{S(e.target.value)},onBlur:w}),t.unit===void 0?null:e(`span`,{className:`dsu-unit`},t.unit)),v?e(`p`,{className:`dsu-error`,id:h},t.invalidHint):null))}function b(t){let[i,a]=s(t.value),[o,c]=s(!1),l=r(),u=t.hint===void 0?void 0:`${l}-hint`;n(()=>{o||a(t.value)},[t.value,o]);let d={id:l,className:`dsu-input${t.multiline===!0?` dsu-textarea`:``}${t.monospace===!0?` dsu-mono`:``}`,value:i,placeholder:t.placeholder,"aria-describedby":u,"data-field":t.field,disabled:t.disabled===!0,onFocus:()=>{c(!0)},onBlur:()=>{c(!1)},onChange:e=>{a(e.target.value),t.onChange(e.target.value)}};return e(_,{...t,controlId:l},t.multiline===!0?e(`textarea`,d):e(`input`,d))}function x(t){let i=t.separator??`comma`,a=t.value.join(i===`newline`?`
84
82
  `:`, `),[o,c]=s(a),[l,u]=s(!1),d=r(),f=t.hint===void 0?void 0:`${d}-hint`;n(()=>{l||c(a)},[a,l]);let p=e=>e.split(i===`newline`?`
85
- `:`,`).map(e=>e.trim()).filter(e=>e.length>0),m={id:d,className:`dsu-input${i===`newline`?` dsu-textarea dsu-mono`:``}`,value:o,placeholder:t.placeholder,"aria-describedby":f,"data-field":t.field,disabled:t.disabled===!0,onFocus:()=>{u(!0)},onBlur:()=>{u(!1)},onChange:e=>{c(e.target.value),t.onChange(p(e.target.value))}};return e(g,{...t,controlId:d},e(i===`newline`?`textarea`:`input`,m))}function x(){return`happyDOM`in globalThis?!1:typeof CSS<`u`&&typeof CSS.supports==`function`&&CSS.supports(`position-area: block-end`)}function S(c){let[l,u]=s(!1),[d,f]=s(0),p=o(null),m=o(null),h=r(),_=r(),v=`--dsu-${_.replaceAll(/[^a-zA-Z0-9]/gu,``)}`,y=c.hint===void 0?void 0:`${_}-hint`,{disabled:b,value:S,options:C,onChange:w}=c,T=a(()=>C.map((e,t)=>({option:e,index:t})).filter(e=>e.option.disabled!==!0),[C]),E=C.find(e=>e.value===S)?.label??S,D=C[d],O=D!==void 0&&D.disabled!==!0,k=t(()=>{let e=p.current,t=m.current;
83
+ `:`,`).map(e=>e.trim()).filter(e=>e.length>0),m={id:d,className:`dsu-input${i===`newline`?` dsu-textarea dsu-mono`:``}`,value:o,placeholder:t.placeholder,"aria-describedby":f,"data-field":t.field,disabled:t.disabled===!0,onFocus:()=>{u(!0)},onBlur:()=>{u(!1)},onChange:e=>{c(e.target.value),t.onChange(p(e.target.value))}};return e(_,{...t,controlId:d},e(i===`newline`?`textarea`:`input`,m))}function S(c){let[l,u]=s(!1),[d,f]=s(0),p=o(null),h=o(null),g=r(),v=r(),y=`--dsu-${v.replaceAll(/[^a-zA-Z0-9]/gu,``)}`,b=c.hint===void 0?void 0:`${v}-hint`,{disabled:x,value:S,options:C,onChange:w}=c,T=a(()=>C.map((e,t)=>({option:e,index:t})).filter(e=>e.option.disabled!==!0),[C]),E=C.find(e=>e.value===S)?.label??S,D=C[d],O=D!==void 0&&D.disabled!==!0,k=t(()=>{let e=p.current,t=h.current;
86
84
  /* v8 ignore next 3 -- place 只在 open 的 layout effect 里调用;refs 为空只可能是卸载竞态 */
87
- if(e===null||t===null)return;let n=e.getBoundingClientRect(),r=Math.min(n.width,window.innerWidth-16);if(t.style.width=`${r}px`,x())return;t.style.marginTop=`0px`;let{left:i}=n;i+r>window.innerWidth-8&&(i=window.innerWidth-r-8);let a=t.offsetHeight,o=n.bottom+6;o+a>window.innerHeight-8&&(o=n.top-a-6);let s=Math.max(8,window.innerWidth-r-8),c=Math.max(8,window.innerHeight-a-8);t.style.left=`${Math.min(Math.max(8,i),s)}px`,t.style.top=`${Math.min(Math.max(8,o),c)}px`},[]),A=t(()=>{u(!1)},[]),j=t(()=>{if(b===!0)return;let e=T.find(e=>e.option.value===S)??T.at(0);e!==void 0&&(f(e.index),u(!0))},[b,S,T]),M=t(e=>{
85
+ if(e===null||t===null)return;let n=e.getBoundingClientRect(),r=Math.min(n.width,window.innerWidth-16);if(t.style.width=`${r}px`,m())return;t.style.marginTop=`0px`;let{left:i}=n;i+r>window.innerWidth-8&&(i=window.innerWidth-r-8);let a=t.offsetHeight,o=n.bottom+6;o+a>window.innerHeight-8&&(o=n.top-a-6);let s=Math.max(8,window.innerWidth-r-8),c=Math.max(8,window.innerHeight-a-8);t.style.left=`${Math.min(Math.max(8,i),s)}px`,t.style.top=`${Math.min(Math.max(8,o),c)}px`},[]),A=t(()=>{u(!1)},[]),j=t(()=>{if(x===!0)return;let e=T.find(e=>e.option.value===S)??T.at(0);e!==void 0&&(f(e.index),u(!0))},[x,S,T]),M=t(e=>{
88
86
  /* v8 ignore next 3 -- 浮层只能经由 openMenu 打开;组件禁用时打不开 */
89
- if(b===!0)return;let t=C[e];
87
+ if(x===!0)return;let t=C[e];
90
88
  /* v8 ignore next 3 -- disabled 选项在浏览器里不派发 click;越界下标由调用方保证 */
91
- t!==void 0&&t.disabled!==!0&&(w(t.value),A(),p.current?.focus())},[b,C,w,A]),N=t(e=>{let t=T.findIndex(e=>e.index===d);
89
+ t!==void 0&&t.disabled!==!0&&(w(t.value),A(),p.current?.focus())},[x,C,w,A]),N=t(e=>{let t=T.findIndex(e=>e.index===d);
92
90
  /* v8 ignore next 5 -- open 时归一 effect 已保证 enabled 非空且 active 落在可用项上 */
93
91
  if(t===-1||T.length===0)return;let n=(t+e+T.length)%T.length,r=T[n];
94
92
  /* v8 ignore next 3 -- next 由取模保证在界内 */
95
- r!==void 0&&f(r.index)},[d,T]);i(()=>{if(!l)return()=>{};k();let e=e=>{let t=e.target;m.current?.contains(t)!==!0&&p.current?.contains(t)!==!0&&A()},t=e=>{let{target:t}=e;t instanceof Node&&m.current?.contains(t)===!0||A()},n=()=>{k()};return document.addEventListener(`pointerdown`,e,!0),window.addEventListener(`scroll`,t,!0),window.addEventListener(`resize`,n),()=>{document.removeEventListener(`pointerdown`,e,!0),window.removeEventListener(`scroll`,t,!0),window.removeEventListener(`resize`,n)}},[l,c.options,k,A]),n(()=>{if(!l)return;if(b===!0||T.length===0){A();return}if(T.some(e=>e.index===d))return;let e=T.find(e=>e.option.value===S)??T.at(0);
93
+ r!==void 0&&f(r.index)},[d,T]);i(()=>{if(!l)return()=>{};k();let e=e=>{let t=e.target;h.current?.contains(t)!==!0&&p.current?.contains(t)!==!0&&A()},t=e=>{let{target:t}=e;t instanceof Node&&h.current?.contains(t)===!0||A()},n=()=>{k()};return document.addEventListener(`pointerdown`,e,!0),window.addEventListener(`scroll`,t,!0),window.addEventListener(`resize`,n),()=>{document.removeEventListener(`pointerdown`,e,!0),window.removeEventListener(`scroll`,t,!0),window.removeEventListener(`resize`,n)}},[l,c.options,k,A]),n(()=>{if(!l)return;if(x===!0||T.length===0){A();return}if(T.some(e=>e.index===d))return;let e=T.find(e=>e.option.value===S)??T.at(0);
96
94
  /* v8 ignore next 3 -- enabled 非空在上一分支已保证 */
97
- e!==void 0&&f(e.index)},[l,b,S,C,d,A,T]),i(()=>{if(!l)return;let e=m.current;
95
+ e!==void 0&&f(e.index)},[l,x,S,C,d,A,T]),i(()=>{if(!l)return;let e=h.current;
98
96
  /* v8 ignore next 2 -- 同上,menu 在 open 的 commit 之后必然已挂上 */
99
97
  if(e===null)return;let t=e.querySelector(`[data-active="true"]`);
100
98
  /* v8 ignore next 2 -- active 已由归一 effect 保证落在可用项上 */
101
99
  t!==null&&(t.offsetTop<e.scrollTop?e.scrollTop=t.offsetTop:t.offsetTop+t.offsetHeight>e.scrollTop+e.clientHeight&&(e.scrollTop=t.offsetTop+t.offsetHeight-e.clientHeight))},[l,d,C]);let P=e=>{if(e.key===`ArrowDown`||e.key===`ArrowUp`){if(e.preventDefault(),!l){j();return}N(e.key===`ArrowDown`?1:-1)}else if(e.key===`Home`||e.key===`End`){if(e.preventDefault(),l||j(),T.length===0)return;let t=e.key===`Home`?T.at(0):T.at(-1);
102
100
  /* v8 ignore next 3 -- enabled 为空已在上一条返回 */
103
- if(t===void 0)return;f(t.index)}else if(e.key===`Enter`||e.key===` `){if(e.preventDefault(),!l){j();return}M(d)}else(e.key===`Tab`||e.key===`Escape`)&&A()},F=[],I;for(let[t,n]of C.entries())n.group!==I&&(n.group!==void 0&&F.push(e(`div`,{key:`group-${t}-${n.group}`,className:`dsu-optionGroup`,role:`presentation`},n.group)),I=n.group),F.push(e(`button`,{key:`option-${n.value}`,type:`button`,role:`option`,id:`${h}-${t}`,className:`dsu-option`,tabIndex:-1,"aria-selected":n.value===c.value,"data-active":t===d,disabled:n.disabled===!0,onMouseDown:e=>{e.preventDefault()},onClick:()=>{M(t)},onMouseEnter:()=>{
101
+ if(t===void 0)return;f(t.index)}else if(e.key===`Enter`||e.key===` `){if(e.preventDefault(),!l){j();return}M(d)}else(e.key===`Tab`||e.key===`Escape`)&&A()},F=[],I;for(let[t,n]of C.entries())n.group!==I&&(n.group!==void 0&&F.push(e(`div`,{key:`group-${t}-${n.group}`,className:`dsu-optionGroup`,role:`presentation`},n.group)),I=n.group),F.push(e(`button`,{key:`option-${n.value}`,type:`button`,role:`option`,id:`${g}-${t}`,className:`dsu-option`,tabIndex:-1,"aria-selected":n.value===c.value,"data-active":t===d,disabled:n.disabled===!0,onMouseDown:e=>{e.preventDefault()},onClick:()=>{M(t)},onMouseEnter:()=>{
104
102
  /* v8 ignore next -- happy-dom 对 disabled 元素不派发 mouse 事件;浏览器实现各有差异,保留禁用检查 */
105
- n.disabled!==!0&&f(t)}},e(`span`,null,n.label),e(`svg`,{className:`dsu-check`,width:14,height:14,viewBox:`0 0 14 14`,"aria-hidden":!0},e(`path`,{d:`M2.5 7.5l3 3 6-7`,fill:`none`,stroke:`currentColor`,strokeWidth:1.5,strokeLinecap:`round`,strokeLinejoin:`round`}))));return e(g,{...c,controlId:_},e(`div`,{className:`dsu-selectWrap`,onBlur:e=>{let t=e.relatedTarget;t instanceof Node&&e.currentTarget.contains(t)||A()}},e(`button`,{ref:p,type:`button`,id:_,style:{anchorName:v},className:`dsu-select dsu-input`,role:`combobox`,"aria-haspopup":`listbox`,"aria-expanded":l,"aria-controls":`${h}-list`,"aria-activedescendant":l&&O?`${h}-${d}`:void 0,"aria-describedby":y,"data-field":c.field,disabled:b===!0||C.length===0,onClick:()=>{l?A():j()},onKeyDown:P},e(`span`,{className:`dsu-selectValue`},E),e(`svg`,{className:`dsu-chevron`,width:14,height:14,viewBox:`0 0 14 14`,"aria-hidden":!0},e(`path`,{d:`M3 5l4 4 4-4`,fill:`none`,stroke:`currentColor`,strokeWidth:1.5,strokeLinecap:`round`,strokeLinejoin:`round`}))),e(`div`,{ref:m,id:`${h}-list`,role:`listbox`,className:`dsu-menu`,style:{positionAnchor:v,positionTryFallbacks:`flip-block, flip-inline`},"aria-label":c.label,hidden:!l},...F)))}function C(t){let n=!t.writable||t.busy||!t.dirty,r;return r=t.error===null?t.writable?t.dirty?t.dirtyLabel:t.cleanLabel:t.readOnlyLabel:t.error,e(`div`,{className:`dsu-savebar`,"data-dirty":t.dirty},e(`button`,{type:`button`,className:`dsu-btn dsu-btnPrimary`,"data-field":`save`,disabled:n,onClick:t.onSave},t.busy?t.savingLabel:t.saveLabel),e(`button`,{type:`button`,className:`dsu-btn`,"data-field":`discard`,disabled:n,onClick:t.onDiscard},t.revertLabel),e(`span`,{className:t.error===null?`dsu-status`:`dsu-status dsu-statusError`},r))}export{y as a,h as c,f as d,d as f,c as h,v as i,m as l,l as m,S as n,_ as o,p,b as r,g as s,C as t,u};
103
+ n.disabled!==!0&&f(t)}},e(`span`,null,n.label),e(`svg`,{className:`dsu-check`,width:14,height:14,viewBox:`0 0 14 14`,"aria-hidden":!0},e(`path`,{d:`M2.5 7.5l3 3 6-7`,fill:`none`,stroke:`currentColor`,strokeWidth:1.5,strokeLinecap:`round`,strokeLinejoin:`round`}))));return e(_,{...c,controlId:v},e(`div`,{className:`dsu-selectWrap`,onBlur:e=>{let t=e.relatedTarget;t instanceof Node&&e.currentTarget.contains(t)||A()}},e(`button`,{ref:p,type:`button`,id:v,style:{anchorName:y},className:`dsu-select dsu-input`,role:`combobox`,"aria-haspopup":`listbox`,"aria-expanded":l,"aria-controls":`${g}-list`,"aria-activedescendant":l&&O?`${g}-${d}`:void 0,"aria-describedby":b,"data-field":c.field,disabled:x===!0||C.length===0,onClick:()=>{l?A():j()},onKeyDown:P},e(`span`,{className:`dsu-selectValue`},E),e(`svg`,{className:`dsu-chevron`,width:14,height:14,viewBox:`0 0 14 14`,"aria-hidden":!0},e(`path`,{d:`M3 5l4 4 4-4`,fill:`none`,stroke:`currentColor`,strokeWidth:1.5,strokeLinecap:`round`,strokeLinejoin:`round`}))),e(`div`,{ref:h,id:`${g}-list`,role:`listbox`,className:`dsu-menu`,style:{positionAnchor:y,positionTryFallbacks:`flip-block, flip-inline`},"aria-label":c.label,hidden:!l},...F)))}function C(t){let n=!t.writable||t.busy||!t.dirty,r;return r=t.error===null?t.writable?t.dirty?t.dirtyLabel:t.cleanLabel:t.readOnlyLabel:t.error,e(`div`,{className:`dsu-savebar`,"data-dirty":t.dirty},e(`button`,{type:`button`,className:`dsu-btn dsu-btnPrimary`,"data-field":`save`,disabled:n,onClick:t.onSave},t.busy?t.savingLabel:t.saveLabel),e(`button`,{type:`button`,className:`dsu-btn`,"data-field":`discard`,disabled:n,onClick:t.onDiscard},t.revertLabel),e(`span`,{role:`status`,className:t.error===null?`dsu-status`:`dsu-status dsu-statusError`},r))}export{b as a,g as c,f as d,d as f,c as h,y as i,h as l,l as m,S as n,v as o,p,x as r,_ as s,C as t,u};
package/dist/card-ui.js CHANGED
@@ -1 +1 @@
1
- export{h as CARD_UI_CSS,u as CardForm,s as Field,d as FieldGrid,c as FieldLabel,l as InfoTip,r as ListField,i as NumberField,t as SaveBar,f as Section,n as SelectField,p as StatusBar,a as TextField,o as ToggleField,m as splitHint}from"./card-ui-D2cuQvLS.js";
1
+ export{h as CARD_UI_CSS,u as CardForm,s as Field,d as FieldGrid,c as FieldLabel,l as InfoTip,r as ListField,i as NumberField,t as SaveBar,f as Section,n as SelectField,p as StatusBar,a as TextField,o as ToggleField,m as splitHint}from"./card-ui-CV_X0CV5.js";
@@ -1 +1 @@
1
- import{a as e,c as t,d as n,f as r,h as i,i as a,l as o,m as s,n as c,o as l,p as u,r as d,s as f,t as p,u as m}from"./card-ui-D2cuQvLS.js";import{diffTouched as h}from"./card-diff.js";import{claimApply as g}from"./card-apply.js";import{fieldOf as _,isRecord as v}from"./record.js";import{errorText as y}from"./errors.js";const b=`@jayyuen66/dsh-plugin-shared`;function x(){}export{i as CARD_UI_CSS,m as CardForm,f as Field,n as FieldGrid,t as FieldLabel,o as InfoTip,d as ListField,a as NumberField,p as SaveBar,r as Section,c as SelectField,u as StatusBar,e as TextField,l as ToggleField,x as apply,g as claimApply,h as diffTouched,y as errorText,_ as fieldOf,v as isRecord,b as name,s as splitHint};
1
+ import{a as e,c as t,d as n,f as r,h as i,i as a,l as o,m as s,n as c,o as l,p as u,r as d,s as f,t as p,u as m}from"./card-ui-CV_X0CV5.js";import{diffTouched as h}from"./card-diff.js";import{claimApply as g}from"./card-apply.js";import{fieldOf as _,isRecord as v}from"./record.js";import{errorText as y}from"./errors.js";const b=`@jayyuen66/dsh-plugin-shared`;function x(){}export{i as CARD_UI_CSS,m as CardForm,f as Field,n as FieldGrid,t as FieldLabel,o as InfoTip,d as ListField,a as NumberField,p as SaveBar,r as Section,c as SelectField,u as StatusBar,e as TextField,l as ToggleField,x as apply,g as claimApply,h as diffTouched,y as errorText,_ as fieldOf,v as isRecord,b as name,s as splitHint};
package/dist/client.js CHANGED
@@ -29,11 +29,11 @@ const CARD_UI_CSS = `
29
29
  .dsu-helpBtn{appearance:none;border:0;background:transparent;padding:0;width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary);cursor:help;border-radius:50%;flex:none}
30
30
  .dsu-help:hover .dsu-helpBtn,.dsu-helpBtn:focus-visible{color:var(--dsw-alias-label-primary)}
31
31
  .dsu-helpBtn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}
32
- .dsu-tip{position:fixed;z-index:60;box-sizing:border-box;max-width:min(340px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:auto;overflow-wrap:anywhere;padding:10px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);box-shadow:0 12px 32px rgb(0 0 0 / 45%);font-size:12px;line-height:1.6;font-weight:400;color:var(--dsw-alias-label-secondary)}
32
+ .dsu-tip{width:max-content;position:fixed;position-area:block-end span-inline-end;justify-self:start;margin-top:8px;z-index:60;box-sizing:border-box;max-width:min(340px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:auto;overflow-wrap:anywhere;padding:10px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);box-shadow:0 12px 32px rgb(0 0 0 / 45%);font-size:12px;line-height:1.6;font-weight:400;color:var(--dsw-alias-label-secondary)}
33
33
  .dsu-input{width:100%;height:34px;padding:0 12px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}
34
34
  .dsu-input:focus-visible{outline:none;border-color:var(--dsw-alias-state-business-primary)}
35
35
  .dsu-input[aria-invalid="true"]{border-color:var(--dsw-alias-state-error-primary)}
36
- .dsu-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}
36
+ .dsu-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed}
37
37
  .dsu-input::placeholder{color:var(--dsw-alias-label-tertiary)}
38
38
  .dsu-textarea{height:auto;min-height:96px;padding:8px 12px;line-height:1.6;resize:vertical}
39
39
  .dsu-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
@@ -54,7 +54,7 @@ const CARD_UI_CSS = `
54
54
  .dsu-selectValue{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
55
55
  .dsu-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s}
56
56
  .dsu-select[aria-expanded="true"] .dsu-chevron{transform:rotate(180deg)}
57
- .dsu-menu{position:fixed;position-area:block-end span-inline-start;justify-self:start;margin-top:6px;z-index:70;padding:4px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);border-radius:12px;box-shadow:0 12px 32px rgb(0 0 0 / 50%);max-height:280px;overflow:auto}
57
+ .dsu-menu{position:fixed;position-area:block-end span-inline-end;justify-self:start;margin-top:6px;z-index:70;padding:4px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);border-radius:12px;box-shadow:0 12px 32px rgb(0 0 0 / 50%);max-height:280px;overflow:auto}
58
58
  .dsu-option{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:1.4;width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 10px;border-radius:8px;cursor:pointer;text-align:left}
59
59
  .dsu-option:hover,.dsu-option[data-active="true"]{background:var(--dsw-alias-bg-layer-3)}
60
60
  .dsu-option:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed}
@@ -118,15 +118,27 @@ function StatusBar(props) {
118
118
  const children = props.items.map((item, index) => (0, react.createElement)("span", { key: index }, index === 0 ? item : `· ${item}`));
119
119
  return (0, react.createElement)("div", { className: "dsu-statusbar" }, ...children);
120
120
  }
121
+ // lib/card-ui/anchor.ts
122
+ /** 浏览器是否支持 CSS anchor 定位(宿主 webview 支持,与宿主 Menu 同一套平台 API)。
123
+ * 支持时浮层位置由 `position-area` 跟随触发器,缩放/平移不漂;happy-dom 等不支持处回退 JS 矩形。
124
+ * 探测的是本组件实际依赖的 `position-area`,不是更早出现的 `anchor-name`:只支持
125
+ * `anchor-name` 的半档实现里浮层不会被真正定位。happy-dom 的 CSS.supports 对 anchor-name
126
+ * 误返 true,但它并不真做 anchor 布局;happy-dom 在 window 上暴露 happyDOM 属性,据此排除
127
+ * (真实浏览器没有该属性)。 */
128
+ function cssAnchorSupported() {
129
+ if ("happyDOM" in globalThis) return false;
130
+ return typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("position-area: block-end");
131
+ }
121
132
  // lib/card-ui/field.ts
122
- /** 说明按钮与浮层:悬停/聚焦显示,点击钉住,Esc 关闭。 */
133
+ /** 说明按钮与浮层:悬停/聚焦显示,点击钉住,Esc 或点击外部关闭。 */
123
134
  function InfoTip(props) {
124
135
  const id = (0, react.useId)();
136
+ const anchorId = `--dsu-tip-${id.replaceAll(/[^a-zA-Z0-9]/gu, "")}`;
125
137
  const [open, setOpen] = (0, react.useState)(false);
126
- const [pinned, setPinned] = (0, react.useState)(false);
127
138
  const hoveredRef = (0, react.useRef)(false);
128
139
  const focusedRef = (0, react.useRef)(false);
129
140
  const pinnedRef = (0, react.useRef)(false);
141
+ const wrapRef = (0, react.useRef)(null);
130
142
  const anchorRef = (0, react.useRef)(null);
131
143
  const tipRef = (0, react.useRef)(null);
132
144
  const hideTimer = (0, react.useRef)(null);
@@ -141,6 +153,8 @@ function InfoTip(props) {
141
153
  const tip = tipRef.current;
142
154
  /* v8 ignore next 3 -- place 只在 open 的 layout effect 与挂载期监听器里调用;refs 为空只可能是卸载竞态 */
143
155
  if (anchor === null || tip === null) return;
156
+ if (cssAnchorSupported()) return;
157
+ tip.style.marginTop = "0px";
144
158
  const rect = anchor.getBoundingClientRect();
145
159
  const width = tip.offsetWidth;
146
160
  const height = tip.offsetHeight;
@@ -156,7 +170,6 @@ function InfoTip(props) {
156
170
  const close = (0, react.useCallback)(() => {
157
171
  cancelHide();
158
172
  pinnedRef.current = false;
159
- setPinned(false);
160
173
  setOpen(false);
161
174
  }, [cancelHide]);
162
175
  const show = (0, react.useCallback)(() => {
@@ -174,7 +187,6 @@ function InfoTip(props) {
174
187
  }, [cancelHide]);
175
188
  const updatePinned = (0, react.useCallback)((next) => {
176
189
  pinnedRef.current = next;
177
- setPinned(next);
178
190
  }, []);
179
191
  (0, react.useLayoutEffect)(() => {
180
192
  if (!open) return () => {};
@@ -191,9 +203,16 @@ function InfoTip(props) {
191
203
  const onResize = () => {
192
204
  place();
193
205
  };
206
+ const onPointerDown = (event) => {
207
+ const target = event.target;
208
+ if (wrapRef.current?.contains(target) === true) return;
209
+ close();
210
+ };
211
+ document.addEventListener("pointerdown", onPointerDown, true);
194
212
  window.addEventListener("scroll", onScroll, true);
195
213
  window.addEventListener("resize", onResize);
196
214
  return () => {
215
+ document.removeEventListener("pointerdown", onPointerDown, true);
197
216
  window.removeEventListener("scroll", onScroll, true);
198
217
  window.removeEventListener("resize", onResize);
199
218
  };
@@ -218,9 +237,10 @@ function InfoTip(props) {
218
237
  ref: anchorRef,
219
238
  type: "button",
220
239
  className: "dsu-helpBtn",
240
+ style: { anchorName: anchorId },
221
241
  "aria-label": `${props.label} 说明`,
222
- "aria-describedby": id,
223
- "aria-pressed": pinned,
242
+ "aria-expanded": open,
243
+ "aria-controls": id,
224
244
  onClick: () => {
225
245
  if (pinnedRef.current) {
226
246
  close();
@@ -257,10 +277,15 @@ function InfoTip(props) {
257
277
  id,
258
278
  role: "tooltip",
259
279
  className: "dsu-tip",
280
+ style: {
281
+ positionAnchor: anchorId,
282
+ positionTryFallbacks: "flip-block, flip-inline"
283
+ },
260
284
  hidden: !open
261
285
  }, props.detail);
262
286
  const labelNode = props.showLabel === false ? null : (0, react.createElement)("span", { className: `dsu-label${props.compact === true ? " dsu-labelMono" : ""}` }, props.label);
263
287
  return (0, react.createElement)("span", {
288
+ ref: wrapRef,
264
289
  className: "dsu-help",
265
290
  onMouseEnter: () => {
266
291
  hoveredRef.current = true;
@@ -330,21 +355,25 @@ function ToggleField(props) {
330
355
  }
331
356
  }, (0, react.createElement)("span", { className: "dsu-knob" })));
332
357
  }
333
- /** 数字行:以显示单位读写,越界值不写入。 */
358
+ /** 数字行:以显示单位读写,越界值不写入,失焦后才提示。 */
334
359
  function NumberField(props) {
335
360
  const text = typeof props.value === "number" && Number.isFinite(props.value) ? String(props.value) : "";
336
361
  const [draft, setDraft] = (0, react.useState)(text);
337
362
  const [invalid, setInvalid] = (0, react.useState)(false);
363
+ const [showInvalid, setShowInvalid] = (0, react.useState)(false);
338
364
  const lastEmitted = (0, react.useRef)(void 0);
339
365
  const controlId = (0, react.useId)();
340
366
  const errorId = (0, react.useId)();
341
- const describedByIds = [props.hint === void 0 ? void 0 : `${controlId}-hint`, invalid ? errorId : void 0].filter((entry) => entry !== void 0);
367
+ const hintId = props.hint === void 0 ? void 0 : `${controlId}-hint`;
368
+ const showError = invalid && showInvalid;
369
+ const describedByIds = [hintId, showError ? errorId : void 0].filter((entry) => entry !== void 0);
342
370
  const describedBy = describedByIds.length === 0 ? void 0 : describedByIds.join(" ");
343
371
  (0, react.useEffect)(() => {
344
372
  if (props.value === lastEmitted.current) return;
345
373
  lastEmitted.current = props.value;
346
374
  setDraft(text);
347
375
  setInvalid(false);
376
+ setShowInvalid(false);
348
377
  }, [props.value, text]);
349
378
  const accepts = (value) => {
350
379
  const min = props.min ?? -1e400;
@@ -356,6 +385,7 @@ function NumberField(props) {
356
385
  return Math.abs(offset - Math.round(offset)) < 1e-9;
357
386
  };
358
387
  const change = (next) => {
388
+ setShowInvalid(false);
359
389
  setDraft(next);
360
390
  if (next === "") {
361
391
  setInvalid(false);
@@ -376,6 +406,10 @@ function NumberField(props) {
376
406
  if (draft === "" || invalid) return;
377
407
  setDraft(String(Number(draft)));
378
408
  };
409
+ const blur = () => {
410
+ setShowInvalid(invalid);
411
+ normalize();
412
+ };
379
413
  return (0, react.createElement)(Field, {
380
414
  ...props,
381
415
  controlId
@@ -389,15 +423,15 @@ function NumberField(props) {
389
423
  max: props.max,
390
424
  step: props.step,
391
425
  placeholder: props.placeholder,
392
- "aria-invalid": invalid || void 0,
426
+ "aria-invalid": showError || void 0,
393
427
  "aria-describedby": describedBy,
394
428
  "data-field": props.field,
395
429
  disabled: props.disabled === true,
396
430
  onChange: (event) => {
397
431
  change(event.target.value);
398
432
  },
399
- onBlur: normalize
400
- }), props.unit === void 0 ? null : (0, react.createElement)("span", { className: "dsu-unit" }, props.unit)), invalid ? (0, react.createElement)("p", {
433
+ onBlur: blur
434
+ }), props.unit === void 0 ? null : (0, react.createElement)("span", { className: "dsu-unit" }, props.unit)), showError ? (0, react.createElement)("p", {
401
435
  className: "dsu-error",
402
436
  id: errorId
403
437
  }, props.invalidHint) : null));
@@ -472,16 +506,6 @@ function ListField(props) {
472
506
  }, separator === "newline" ? (0, react.createElement)("textarea", shared) : (0, react.createElement)("input", shared));
473
507
  }
474
508
  // lib/card-ui/select.ts
475
- /** 浏览器是否支持 CSS anchor 定位(宿主 webview 支持,与宿主 Menu 同一套平台 API)。
476
- * 支持时浮层位置由 `position-area` 跟随触发器,缩放/平移不漂;happy-dom 等不支持处回退 JS 矩形。
477
- * 探测的是本组件实际依赖的 `position-area`,不是更早出现的 `anchor-name`:只支持
478
- * `anchor-name` 的半档实现里浮层不会被真正定位。happy-dom 的 CSS.supports 对 anchor-name
479
- * 误返 true,但它并不真做 anchor 布局;happy-dom 在 window 上暴露 happyDOM 属性,据此排除
480
- * (真实浏览器没有该属性)。 */
481
- function cssAnchorSupported() {
482
- if ("happyDOM" in globalThis) return false;
483
- return typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("position-area: block-end");
484
- }
485
509
  /** 自定义下拉:浮层固定定位,键盘可选,点击外部关闭。 */
486
510
  function SelectField(props) {
487
511
  const [open, setOpen] = (0, react.useState)(false);
@@ -790,7 +814,10 @@ function SaveBar(props) {
790
814
  "data-field": "discard",
791
815
  disabled,
792
816
  onClick: props.onDiscard
793
- }, props.revertLabel), (0, react.createElement)("span", { className: props.error === null ? "dsu-status" : "dsu-status dsu-statusError" }, status));
817
+ }, props.revertLabel), (0, react.createElement)("span", {
818
+ role: "status",
819
+ className: props.error === null ? "dsu-status" : "dsu-status dsu-statusError"
820
+ }, status));
794
821
  }
795
822
  // lib/card-diff.ts
796
823
  /**
@@ -1 +1 @@
1
- import{isRecord as e}from"./record.js";import{parsePatch as t}from"@deepseek-ai/dsh-fs-patch";function n(t){return e(t)&&t.isError===!0}function r(e){return e===!0}function i(t){return e(t.data)}function a(e){return typeof e==`string`&&e.length>0?e:void 0}function o(e){return typeof e==`string`&&e.length>0?e:void 0}function s(t){if(typeof t==`string`)try{let n=JSON.parse(t);return{arguments:e(n)?n:{},badArguments:!1}}catch{return{arguments:{},badArguments:!0}}return{arguments:e(t)?t:{},badArguments:!1}}function c(e,t){let n;if(!i(e))return n;if(e.type===`tool/call`){let r=o(e.data.name);if(r!==void 0){let i=s(e.data.arguments);n={name:r,callId:a(e.data.callId),arguments:i.arguments,badArguments:i.badArguments,seq:t}}}else if(e.type===`tool/ptc-dispatch-start`){let r=a(e.data.subCallId),i=o(e.data.name);if(r!==void 0&&i!==void 0){let a=s(e.data.arguments);n={name:i,callId:r,arguments:a.arguments,badArguments:a.badArguments,seq:t}}}return n}function l(t,o){let s;if(!i(t))return s;if(t.type===`tool/result`){let r=t.data.message,i=e(r)?r.source:void 0;s={callId:e(i)?a(i.callId):void 0,isError:n(r),seq:o}}else if(t.type===`tool/ptc-dispatch`){let e=a(t.data.subCallId);e!==void 0&&(s={callId:e,isError:r(t.data.isError),seq:o})}return s}function u(e,t){return{call:c(e,t),result:l(e,t)}}function d(t){let n=[],r=[];for(let[i,a]of t.entries())if(e(a)){let{call:e,result:t}=u(a,i);e!==void 0&&n.push(e),t!==void 0&&r.push(t)}return{calls:n,results:r}}function f(e,t){let n=e[t];return typeof n==`string`?n:void 0}function p(e){let n=e?.input;if(typeof n!=`string`||!n.includes(`*** Begin Patch`))return[];try{return t(n).map(e=>e.kind===`update`?e.moveTo===void 0?{kind:`update`,path:e.path}:{kind:`update`,path:e.path,moveTo:e.moveTo}:{kind:e.kind,path:e.path})}catch{return[]}}function m(e){let t=e.arguments;return e.name===`apply_patch`?{kind:`write`,path:p(t)[0]?.path}:e.name===`str_replace_editor`?t.command===`view`?{kind:`read-view`,path:f(t,`path`)}:{kind:`write`,path:f(t,`path`)}:{kind:`write`,path:f(t,`file_path`)}}function h(e){if(e.name===`apply_patch`){let t=p(e.arguments).flatMap(e=>e.kind===`update`&&e.moveTo!==void 0?[e.path,e.moveTo]:[e.path]);return t.filter((e,n)=>e.length>0&&t.indexOf(e)===n)}let t=m(e);return t.kind===`write`&&t.path!==void 0?[t.path]:[]}export{p as applyPatchTargetsOf,m as editPathOf,h as editPathsOf,d as scanToolEvents,u as toolEventRowsOf};
1
+ import{isRecord as e}from"./record.js";import{parsePatch as t}from"@deepseek-ai/dsh-fs-patch";function n(t){return e(t)&&t.isError===!0}function r(t){let n;if(e(t)){let{name:e,code:r,reason:i}=t;typeof e==`string`&&e!==``&&typeof r==`string`&&r!==``&&(n=typeof i==`string`?{name:e,code:r,reason:i}:{name:e,code:r})}return n}function i(e){return e===!0}function a(t){return e(t.data)}function o(e){return typeof e==`string`&&e.length>0?e:void 0}function s(e){return typeof e==`string`&&e.length>0?e:void 0}function c(t){if(typeof t==`string`)try{let n=JSON.parse(t);return{arguments:e(n)?n:{},badArguments:!1}}catch{return{arguments:{},badArguments:!0}}return{arguments:e(t)?t:{},badArguments:!1}}function l(e,t){let n;if(!a(e))return n;if(e.type===`tool/call`){let r=s(e.data.name);if(r!==void 0){let i=c(e.data.arguments);n={name:r,callId:o(e.data.callId),arguments:i.arguments,badArguments:i.badArguments,seq:t}}}else if(e.type===`tool/ptc-dispatch-start`){let r=o(e.data.subCallId),i=s(e.data.name);if(r!==void 0&&i!==void 0){let a=c(e.data.arguments);n={name:i,callId:r,arguments:a.arguments,badArguments:a.badArguments,seq:t}}}return n}function u(t,s){let c;if(!a(t))return c;if(t.type===`tool/result`){let i=t.data.message,a=e(i)?i.source:void 0;c={callId:e(a)?o(a.callId):void 0,isError:n(i),error:r(t.data.error),seq:s}}else if(t.type===`tool/ptc-dispatch`){let e=o(t.data.subCallId);e!==void 0&&(c={callId:e,isError:i(t.data.isError),error:r(t.data.error),seq:s})}return c}function d(e,t){return{call:l(e,t),result:u(e,t)}}function f(t){let n=[],r=[];for(let[i,a]of t.entries())if(e(a)){let{call:e,result:t}=d(a,i);e!==void 0&&n.push(e),t!==void 0&&r.push(t)}return{calls:n,results:r}}function p(e,t){let n=e[t];return typeof n==`string`?n:void 0}function m(e){let n=e?.input;if(typeof n!=`string`||!n.includes(`*** Begin Patch`))return[];try{return t(n).map(e=>e.kind===`update`?e.moveTo===void 0?{kind:`update`,path:e.path}:{kind:`update`,path:e.path,moveTo:e.moveTo}:{kind:e.kind,path:e.path})}catch{return[]}}function h(e){let t=e.arguments;return e.name===`apply_patch`?{kind:`write`,path:m(t)[0]?.path}:e.name===`str_replace_editor`?t.command===`view`?{kind:`read-view`,path:p(t,`path`)}:{kind:`write`,path:p(t,`path`)}:{kind:`write`,path:p(t,`file_path`)}}function g(e){if(e.name===`apply_patch`){let t=m(e.arguments).flatMap(e=>e.kind===`update`&&e.moveTo!==void 0?[e.path,e.moveTo]:[e.path]);return t.filter((e,n)=>e.length>0&&t.indexOf(e)===n)}let t=h(e);return t.kind===`write`&&t.path!==void 0?[t.path]:[]}export{m as applyPatchTargetsOf,h as editPathOf,g as editPathsOf,f as scanToolEvents,d as toolEventRowsOf};
@@ -0,0 +1,7 @@
1
+ /** 浏览器是否支持 CSS anchor 定位(宿主 webview 支持,与宿主 Menu 同一套平台 API)。
2
+ * 支持时浮层位置由 `position-area` 跟随触发器,缩放/平移不漂;happy-dom 等不支持处回退 JS 矩形。
3
+ * 探测的是本组件实际依赖的 `position-area`,不是更早出现的 `anchor-name`:只支持
4
+ * `anchor-name` 的半档实现里浮层不会被真正定位。happy-dom 的 CSS.supports 对 anchor-name
5
+ * 误返 true,但它并不真做 anchor 布局;happy-dom 在 window 上暴露 happyDOM 属性,据此排除
6
+ * (真实浏览器没有该属性)。 */
7
+ export declare function cssAnchorSupported(): boolean;
@@ -12,7 +12,7 @@ export interface FieldBase {
12
12
  /** 控件的 DOM id;字段组件用 `useId` 生成,FieldLabel 用它建立 label↔control 关联。 */
13
13
  controlId?: string;
14
14
  }
15
- /** 说明按钮与浮层:悬停/聚焦显示,点击钉住,Esc 关闭。 */
15
+ /** 说明按钮与浮层:悬停/聚焦显示,点击钉住,Esc 或点击外部关闭。 */
16
16
  export declare function InfoTip(props: {
17
17
  label: string;
18
18
  detail: string;
@@ -5,7 +5,7 @@ export declare function ToggleField(props: FieldBase & {
5
5
  checked: boolean;
6
6
  onChange: (next: boolean) => void;
7
7
  }): ReactElement;
8
- /** 数字行:以显示单位读写,越界值不写入。 */
8
+ /** 数字行:以显示单位读写,越界值不写入,失焦后才提示。 */
9
9
  export declare function NumberField(props: FieldBase & {
10
10
  value: number | undefined;
11
11
  min?: number;
@@ -1,2 +1,2 @@
1
1
  /** 设置卡统一样式。颜色/圆角全部走宿主主题令牌。 */
2
- export declare const CARD_UI_CSS = "\n.dsu-form{display:flex;flex-direction:column}\n.dsu-section{margin-top:24px;padding-top:24px;border-top:.5px solid var(--dsw-alias-border-l1)}\n.dsu-form>.dsu-section:first-child{margin-top:0;padding-top:0;border-top:0}\n.dsu-sectionTitle{margin:0;font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary)}\n.dsu-sectionHint{margin:4px 0 0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}\n.dsu-statusbar{display:flex;flex-wrap:wrap;gap:4px 8px;margin-top:8px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}\n.dsu-field{display:grid;grid-template-columns:minmax(0,1fr) minmax(240px,360px);column-gap:32px;align-items:center;padding:12px 0;border-top:.5px solid var(--dsw-alias-border-l2)}\n.dsu-fieldMeta{display:flex;flex-direction:column;gap:4px;min-width:0}\n.dsu-labelRow{display:flex;align-items:center;gap:6px;min-width:0}\n.dsu-label{font-size:13px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary)}\n.dsu-labelMono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}\n.dsu-hint{margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}\n.dsu-control{min-width:0;display:flex;align-items:center;gap:8px}\n.dsu-fieldWide{grid-template-columns:minmax(0,1fr);align-items:start}\n.dsu-fieldWide .dsu-control{width:100%;max-width:720px;margin-top:2px}\n.dsu-help{display:inline-flex;align-items:center;gap:6px;min-width:0}\n.dsu-helpBtn{appearance:none;border:0;background:transparent;padding:0;width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary);cursor:help;border-radius:50%;flex:none}\n.dsu-help:hover .dsu-helpBtn,.dsu-helpBtn:focus-visible{color:var(--dsw-alias-label-primary)}\n.dsu-helpBtn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}\n.dsu-tip{position:fixed;z-index:60;box-sizing:border-box;max-width:min(340px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:auto;overflow-wrap:anywhere;padding:10px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);box-shadow:0 12px 32px rgb(0 0 0 / 45%);font-size:12px;line-height:1.6;font-weight:400;color:var(--dsw-alias-label-secondary)}\n.dsu-input{width:100%;height:34px;padding:0 12px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}\n.dsu-input:focus-visible{outline:none;border-color:var(--dsw-alias-state-business-primary)}\n.dsu-input[aria-invalid=\"true\"]{border-color:var(--dsw-alias-state-error-primary)}\n.dsu-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}\n.dsu-input::placeholder{color:var(--dsw-alias-label-tertiary)}\n.dsu-textarea{height:auto;min-height:96px;padding:8px 12px;line-height:1.6;resize:vertical}\n.dsu-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.dsu-numberWrap{display:flex;flex-direction:column;gap:4px;width:100%;min-width:0}\n.dsu-number{display:flex;align-items:center;gap:8px;min-width:0}\n.dsu-number .dsu-input{min-width:0}\n.dsu-unit{flex:none;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsu-error{margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary)}\n.dsu-switch{appearance:none;width:36px;height:20px;border-radius:999px;border:0;padding:2px;background:var(--dsw-alias-border-l3);display:flex;align-items:center;cursor:pointer;flex:none;transition:background-color .16s}\n.dsu-knob{width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-label-primary);transition:transform .16s,background-color .16s}\n.dsu-switch[aria-checked=\"true\"]{background:var(--dsw-alias-label-primary)}\n.dsu-switch[aria-checked=\"true\"] .dsu-knob{transform:translateX(16px);background:var(--dsw-alias-label-primary-foreground)}\n.dsu-switch:disabled{opacity:.4;cursor:not-allowed}\n.dsu-switch:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}\n.dsu-selectWrap{position:relative;width:100%;min-width:0}\n.dsu-select{display:flex;align-items:center;justify-content:space-between;gap:8px;text-align:left;cursor:pointer}\n.dsu-select[aria-expanded=\"true\"]{border-color:var(--dsw-alias-state-business-primary)}\n.dsu-selectValue{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsu-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s}\n.dsu-select[aria-expanded=\"true\"] .dsu-chevron{transform:rotate(180deg)}\n.dsu-menu{position:fixed;position-area:block-end span-inline-start;justify-self:start;margin-top:6px;z-index:70;padding:4px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);border-radius:12px;box-shadow:0 12px 32px rgb(0 0 0 / 50%);max-height:280px;overflow:auto}\n.dsu-option{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:1.4;width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 10px;border-radius:8px;cursor:pointer;text-align:left}\n.dsu-option:hover,.dsu-option[data-active=\"true\"]{background:var(--dsw-alias-bg-layer-3)}\n.dsu-option:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed}\n.dsu-option:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}\n.dsu-optionGroup{padding:6px 10px 2px;font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary)}\n.dsu-check{opacity:0;flex:none}\n.dsu-option[aria-selected=\"true\"] .dsu-check{opacity:1}\n.dsu-savebar{display:flex;align-items:center;gap:8px;padding:12px 0;margin-top:24px;border-top:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base)}\n.dsu-savebar[data-dirty=\"true\"]{position:sticky;bottom:0;z-index:2;box-shadow:0 -12px 24px rgb(0 0 0 / 35%)}\n.dsu-btn{appearance:none;border:.5px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:1.5;border-radius:var(--dsw-radius-md);padding:5px 14px;cursor:pointer;transition:background-color .12s,border-color .12s,color .12s,filter .12s}\n.dsu-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l3)}\n.dsu-btn:active:not(:disabled){background:var(--dsw-alias-bg-layer-1)}\n.dsu-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}\n.dsu-btn:disabled{opacity:.4;cursor:not-allowed}\n.dsu-btnPrimary{background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}\n.dsu-btnPrimary:hover:not(:disabled){background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);filter:brightness(.9)}\n.dsu-btnPrimary:active:not(:disabled){background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);filter:brightness(.84)}\n.dsu-btnDanger{color:var(--dsw-alias-state-error-primary)}\n.dsu-btnDanger:hover:not(:disabled){color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2)}\n.dsu-status{margin-left:auto;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsu-statusError{color:var(--dsw-alias-state-error-primary)}\n.dsu-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr));gap:0 24px}\n.dsu-grid>.dsu-field{display:flex;flex-direction:column;align-items:stretch;gap:6px;padding:10px 0;border-top:.5px solid var(--dsw-alias-border-l1)}\n.dsu-grid>.dsu-field .dsu-control{width:100%}\n@media (max-width:640px){\n .dsu-field{grid-template-columns:minmax(0,1fr);align-items:start}\n .dsu-control{width:100%;max-width:720px}\n .dsu-section{margin-top:20px;padding-top:20px}\n}\n@media (prefers-reduced-motion:reduce){\n .dsu-switch,.dsu-knob,.dsu-chevron,.dsu-btn{transition:none}\n}\n";
2
+ export declare const CARD_UI_CSS = "\n.dsu-form{display:flex;flex-direction:column}\n.dsu-section{margin-top:24px;padding-top:24px;border-top:.5px solid var(--dsw-alias-border-l1)}\n.dsu-form>.dsu-section:first-child{margin-top:0;padding-top:0;border-top:0}\n.dsu-sectionTitle{margin:0;font-size:15px;font-weight:600;line-height:1.4;color:var(--dsw-alias-label-primary)}\n.dsu-sectionHint{margin:4px 0 0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}\n.dsu-statusbar{display:flex;flex-wrap:wrap;gap:4px 8px;margin-top:8px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}\n.dsu-field{display:grid;grid-template-columns:minmax(0,1fr) minmax(240px,360px);column-gap:32px;align-items:center;padding:12px 0;border-top:.5px solid var(--dsw-alias-border-l2)}\n.dsu-fieldMeta{display:flex;flex-direction:column;gap:4px;min-width:0}\n.dsu-labelRow{display:flex;align-items:center;gap:6px;min-width:0}\n.dsu-label{font-size:13px;font-weight:500;line-height:1.5;color:var(--dsw-alias-label-primary)}\n.dsu-labelMono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}\n.dsu-hint{margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary)}\n.dsu-control{min-width:0;display:flex;align-items:center;gap:8px}\n.dsu-fieldWide{grid-template-columns:minmax(0,1fr);align-items:start}\n.dsu-fieldWide .dsu-control{width:100%;max-width:720px;margin-top:2px}\n.dsu-help{display:inline-flex;align-items:center;gap:6px;min-width:0}\n.dsu-helpBtn{appearance:none;border:0;background:transparent;padding:0;width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;color:var(--dsw-alias-label-tertiary);cursor:help;border-radius:50%;flex:none}\n.dsu-help:hover .dsu-helpBtn,.dsu-helpBtn:focus-visible{color:var(--dsw-alias-label-primary)}\n.dsu-helpBtn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}\n.dsu-tip{width:max-content;position:fixed;position-area:block-end span-inline-end;justify-self:start;margin-top:8px;z-index:60;box-sizing:border-box;max-width:min(340px,calc(100vw - 16px));max-height:calc(100vh - 16px);overflow:auto;overflow-wrap:anywhere;padding:10px 12px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);box-shadow:0 12px 32px rgb(0 0 0 / 45%);font-size:12px;line-height:1.6;font-weight:400;color:var(--dsw-alias-label-secondary)}\n.dsu-input{width:100%;height:34px;padding:0 12px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}\n.dsu-input:focus-visible{outline:none;border-color:var(--dsw-alias-state-business-primary)}\n.dsu-input[aria-invalid=\"true\"]{border-color:var(--dsw-alias-state-error-primary)}\n.dsu-input:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed}\n.dsu-input::placeholder{color:var(--dsw-alias-label-tertiary)}\n.dsu-textarea{height:auto;min-height:96px;padding:8px 12px;line-height:1.6;resize:vertical}\n.dsu-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.dsu-numberWrap{display:flex;flex-direction:column;gap:4px;width:100%;min-width:0}\n.dsu-number{display:flex;align-items:center;gap:8px;min-width:0}\n.dsu-number .dsu-input{min-width:0}\n.dsu-unit{flex:none;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsu-error{margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary)}\n.dsu-switch{appearance:none;width:36px;height:20px;border-radius:999px;border:0;padding:2px;background:var(--dsw-alias-border-l3);display:flex;align-items:center;cursor:pointer;flex:none;transition:background-color .16s}\n.dsu-knob{width:16px;height:16px;border-radius:50%;background:var(--dsw-alias-label-primary);transition:transform .16s,background-color .16s}\n.dsu-switch[aria-checked=\"true\"]{background:var(--dsw-alias-label-primary)}\n.dsu-switch[aria-checked=\"true\"] .dsu-knob{transform:translateX(16px);background:var(--dsw-alias-label-primary-foreground)}\n.dsu-switch:disabled{opacity:.4;cursor:not-allowed}\n.dsu-switch:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}\n.dsu-selectWrap{position:relative;width:100%;min-width:0}\n.dsu-select{display:flex;align-items:center;justify-content:space-between;gap:8px;text-align:left;cursor:pointer}\n.dsu-select[aria-expanded=\"true\"]{border-color:var(--dsw-alias-state-business-primary)}\n.dsu-selectValue{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsu-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s}\n.dsu-select[aria-expanded=\"true\"] .dsu-chevron{transform:rotate(180deg)}\n.dsu-menu{position:fixed;position-area:block-end span-inline-end;justify-self:start;margin-top:6px;z-index:70;padding:4px;background:var(--dsw-alias-bg-layer-2);border:.5px solid var(--dsw-alias-border-l3);border-radius:12px;box-shadow:0 12px 32px rgb(0 0 0 / 50%);max-height:280px;overflow:auto}\n.dsu-option{appearance:none;border:0;background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:1.4;width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 10px;border-radius:8px;cursor:pointer;text-align:left}\n.dsu-option:hover,.dsu-option[data-active=\"true\"]{background:var(--dsw-alias-bg-layer-3)}\n.dsu-option:disabled{color:var(--dsw-alias-label-tertiary);cursor:not-allowed}\n.dsu-option:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:-2px}\n.dsu-optionGroup{padding:6px 10px 2px;font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary)}\n.dsu-check{opacity:0;flex:none}\n.dsu-option[aria-selected=\"true\"] .dsu-check{opacity:1}\n.dsu-savebar{display:flex;align-items:center;gap:8px;padding:12px 0;margin-top:24px;border-top:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base)}\n.dsu-savebar[data-dirty=\"true\"]{position:sticky;bottom:0;z-index:2;box-shadow:0 -12px 24px rgb(0 0 0 / 35%)}\n.dsu-btn{appearance:none;border:.5px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:1.5;border-radius:var(--dsw-radius-md);padding:5px 14px;cursor:pointer;transition:background-color .12s,border-color .12s,color .12s,filter .12s}\n.dsu-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l3)}\n.dsu-btn:active:not(:disabled){background:var(--dsw-alias-bg-layer-1)}\n.dsu-btn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}\n.dsu-btn:disabled{opacity:.4;cursor:not-allowed}\n.dsu-btnPrimary{background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}\n.dsu-btnPrimary:hover:not(:disabled){background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);filter:brightness(.9)}\n.dsu-btnPrimary:active:not(:disabled){background:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary);filter:brightness(.84)}\n.dsu-btnDanger{color:var(--dsw-alias-state-error-primary)}\n.dsu-btnDanger:hover:not(:disabled){color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2)}\n.dsu-status{margin-left:auto;font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.dsu-statusError{color:var(--dsw-alias-state-error-primary)}\n.dsu-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr));gap:0 24px}\n.dsu-grid>.dsu-field{display:flex;flex-direction:column;align-items:stretch;gap:6px;padding:10px 0;border-top:.5px solid var(--dsw-alias-border-l1)}\n.dsu-grid>.dsu-field .dsu-control{width:100%}\n@media (max-width:640px){\n .dsu-field{grid-template-columns:minmax(0,1fr);align-items:start}\n .dsu-control{width:100%;max-width:720px}\n .dsu-section{margin-top:20px;padding-top:20px}\n}\n@media (prefers-reduced-motion:reduce){\n .dsu-switch,.dsu-knob,.dsu-chevron,.dsu-btn{transition:none}\n}\n";
@@ -71,10 +71,31 @@ export interface ToolCallRecord {
71
71
  */
72
72
  readonly seq: number;
73
73
  }
74
+ /**
75
+ * 失败的结构化身份:官方 `tool/result` 与 `tool/ptc-dispatch` 的 `data.error`
76
+ * (`{ name, code, reason? }`,只允许出现在 `isError: true` 的结算旁)。
77
+ *
78
+ * 复制而不是引用官方 `ToolErrorInfo`:本包的商品面同时服务多个宿主版本,而错误码是
79
+ * **持久化契约**(官方把 `[name, code]` 记在日志里供消费方比较字符串)。形状上逐字段同形,
80
+ * 两边改名会在这里的字段读取处暴露,不需要再引入一层运行时依赖。
81
+ */
82
+ export interface ToolErrorIdentity {
83
+ /** 错误类名(如 `AbortError`、`ApprovalError`)。 */
84
+ readonly name: string;
85
+ /** 稳定错误码(如 `ABORTED`、`APPROVAL_REJECTED`)。 */
86
+ readonly code: string;
87
+ /** 可选的原始用户可见细节;模型侧内容不含它。 */
88
+ readonly reason?: string;
89
+ }
74
90
  /** 一条 tool/result 的成败。同样只读。 */
75
91
  export interface ToolResultRecord {
76
92
  readonly callId: string | undefined;
77
93
  readonly isError: boolean;
94
+ /**
95
+ * 失败身份:`isError` 为真时由结算事件携带;缺失是正常情况(旧宿主、守卫拒绝、
96
+ * 工具体抛出的普通错误都不带身份),消费方必须把它当成"没有分类"而不是成功。
97
+ */
98
+ readonly error: ToolErrorIdentity | undefined;
78
99
  readonly seq: number;
79
100
  }
80
101
  /** 一条事件产出的记录行(call 与 result 互斥;非记录事件两位都是 undefined)。 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jayyuen66/dsh-plugin-shared",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "dsh 自研插件的共享工具库:tool 事件流记账扫描 + 卡片 apply/差异骨架 + 统一设置卡 UI 组件 + 官方作业结局映射 + 项目键派生 + 代理对安全截断。宿主半由各插件 import;客户端半以 ./client 共享行由插件 patch 启用,不由用户单独停用",
5
5
  "keywords": [
6
6
  "cordis",