dsh-form-fill-agent 0.2.0 → 0.2.2

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
@@ -43,7 +43,7 @@ dsh plugin --profile web add dsh-form-fill-agent@latest
43
43
 
44
44
  ## 使用边界与数据保存
45
45
 
46
- 补充要求为模型可读文本;字段或工作表范围请通过向导/工作台修改,不能仅改文字后假定执行范围已同步。任务 schema 5 可读取 schema 1–5;回退旧版本请使用升级前目录副本或独立任务目录。共享内核、Provider 和主包为 0.2.0。
46
+ 补充要求为模型可读文本;字段或工作表范围请通过向导/工作台修改,不能仅改文字后假定执行范围已同步。任务 schema 5 可读取 schema 1–5;回退旧版本请使用升级前目录副本或独立任务目录。主包为 0.2.2,共享内核和 Provider 保持 0.2.0。
47
47
 
48
48
  支持企业完整登记名称或 18 位信用代码;简称、主体不一致或多候选不会自动猜选。仅有信用代码的表格也可作为查询锚点。连接候选检索工具时可检索简称,仍需人工确认;缺少工具时降级人工输入。字段映射采用确定性规则及人工确认,不让模型编造事实。支持普通内部公式、固定区域下拉、基础条件格式和普通表格对象;复杂 Excel 结构和 Word 暂不支持,保真边界见下方安全说明。
49
49
 
@@ -51,7 +51,7 @@ dsh plugin --profile web add dsh-form-fill-agent@latest
51
51
 
52
52
  显式设置 DSH_HOME 时,任务保存至其 form-fill-tasks 目录,默认 24 小时过期;未设置时使用内存。任务保存输入表格和归一化预览,不持久化原始 MCP 响应,可主动删除任务。模型及 MCP 凭据由宿主管理。
53
53
 
54
- 0.2.0 保存任务 schema 5,兼容读取旧 schema 1–4;回退旧版本须使用升级前任务目录副本或新目录,防止丢失已确认的字段/主体设置。
54
+ 0.2.2 保存任务 schema 5,兼容读取旧 schema 1–4;回退旧版本须使用升级前任务目录副本或新目录,防止丢失已确认的字段/主体设置。
55
55
 
56
56
  ## 文档与反馈
57
57
 
package/lib/brand.css CHANGED
@@ -7,7 +7,13 @@
7
7
  .ff-ui.ff-modal-host{background:transparent;padding:0;overflow:hidden}.ff-ui.ff-modal-host> :not(dialog):not(script):not(link){visibility:hidden}html:has(>body.ff-ui.ff-modal-host){background:transparent;color-scheme:normal}.ff-ui #wizard-message{color:var(--error);padding:0 20px;margin:4px 0;flex-shrink:0}
8
8
  [data-composer-card]:has(.ff-ui .ff-prompt){padding-top:48px}.ff-ui .ff-prompt{margin:6px 10px}.ff-ui dialog>.tabs{padding:8px 20px;flex-shrink:0}
9
9
  .ff-ui .ff-launcher{display:flex;align-items:center;gap:8px;color:var(--text);padding:10px 8px;text-decoration:none;border-radius:6px;width:100%}.ff-ui .ff-launcher:hover,.ff-ui .ff-launcher[aria-current=page]{background:var(--soft);color:var(--action)}.ff-ui .ff-launcher .ff-icon{color:var(--brand)}.ff-ui.ff-top{width:100%;min-width:0;padding:4px var(--dsh-sidebar-inline-padding,12px) 4px 0}.ff-ui .ff-shortcuts{display:flex;justify-content:center;gap:6px;flex-wrap:wrap;padding:12px 0}.ff-ui .ff-shortcuts button{display:flex;flex-direction:column;align-items:center;gap:6px;border-color:transparent;background:transparent;color:var(--muted);font-size:12px;min-width:72px}.ff-ui .ff-shortcuts button[aria-pressed=true]{background:var(--soft);color:var(--action)}.ff-ui .ff-prompt{color:var(--action);background:var(--soft);border-color:transparent;display:inline-flex;align-items:center;gap:6px;font-size:13px}
10
- @media(min-width:1100px){[data-form-fill-reserve=true]{padding-right:var(--ff-panel-inset)!important}}
10
+ [data-form-fill-reserve=true]{padding-right:var(--ff-panel-inset)!important}
11
+ .ff-ui .ff-panel-resize{position:absolute;left:-4px;top:0;bottom:0;width:8px;z-index:2;cursor:col-resize;touch-action:none;outline:none}
12
+ .ff-ui .ff-panel-resize:after{content:"";position:absolute;left:3px;top:0;bottom:0;width:3px;background:transparent}
13
+ .ff-ui .ff-panel-resize:hover:after,.ff-ui .ff-panel-resize:focus-visible:after,.ff-ui.ff-resizing .ff-panel-resize:after{background:var(--action)}
14
+ .ff-ui.ff-resizing{user-select:none;cursor:col-resize}.ff-ui.ff-resizing iframe{pointer-events:none}
15
+ .ff-ui.ff-panel{container-type:inline-size}
16
+ @container(max-width:480px){.ff-ui .ff-panel-head{flex-wrap:wrap;padding:10px}.ff-ui .ff-panel-actions{margin-left:auto}.ff-ui .ff-panel-head h2{font-size:14px}}
11
17
  .ff-ui.ff-panel{position:fixed;right:0;top:0;bottom:0;z-index:1000;background:var(--surface);display:flex;flex-direction:column;border-left:1px solid var(--line)}.ff-ui .ff-panel-head{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:12px 16px;border-bottom:1px solid var(--line);flex-shrink:0}.ff-ui .ff-panel-head .ff-mark{width:32px;height:32px}.ff-ui .ff-panel-head .ff-mark svg{width:20px;height:20px}.ff-ui .ff-panel-head h2{font-size:16px}.ff-ui .ff-panel-actions{display:flex;gap:6px}.ff-ui.ff-panel iframe{border:0;width:100%;flex:1;min-height:0}
12
18
  .ff-ui.ff-workbench{margin:0;padding:20px;background:var(--surface);max-width:none}.ff-ui h1{font-size:24px}.ff-ui h2{font-size:18px}.ff-ui p{line-height:1.7}.ff-ui .muted{color:var(--muted);font-size:12px}.ff-ui .toolbar,.ff-ui .controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.ff-ui .toolbar{justify-content:space-between}.ff-ui .tabs{display:flex;gap:10px;overflow:auto;border-bottom:1px solid var(--line);padding:8px 0}.ff-ui .tabs button{white-space:nowrap;background:transparent;border:0;border-bottom:2px solid transparent;border-radius:0;padding:8px 0;font-size:12px;color:var(--muted)}.ff-ui .tabs button[aria-current=true]{border-bottom-color:var(--action);color:var(--action)}.ff-ui .task-tabs{display:flex;gap:20px;border-bottom:1px solid var(--line)}.ff-ui .task-tabs button{border:0;border-bottom:3px solid transparent;border-radius:0;background:transparent;padding:10px 0}.ff-ui .task-tabs button[aria-pressed=true]{color:var(--action);border-bottom-color:var(--action)}.ff-ui .card{padding:16px 0;margin:0;border-bottom:1px solid var(--line)}.ff-ui #status{min-height:26px;color:var(--muted);font-size:13px}.ff-ui #status[data-error=true]{color:var(--error)}.ff-ui .badge{display:inline-block;padding:3px 8px;border-radius:4px;background:var(--soft);color:var(--action);font-size:12px}.ff-ui .badge[data-state=completed]{color:var(--success);background:var(--success-bg)}.ff-ui .badge[data-state=partial],.ff-ui .badge[data-state=interrupted]{color:var(--review);background:var(--review-bg)}.ff-ui .badge[data-state=failed]{color:var(--error)}
13
19
  .ff-ui .scroll{overflow:auto;max-height:520px;border:1px solid var(--line);border-radius:6px}.ff-ui .grid-changed{background:var(--soft);color:var(--action);font-weight:600}.ff-ui table{border-collapse:separate;border-spacing:0;width:100%;font-size:13px}.ff-ui th,.ff-ui td{padding:12px;white-space:nowrap;text-align:left;vertical-align:top;border-bottom:1px solid var(--line);border-right:1px solid var(--line);min-width:100px}.ff-ui th{position:sticky;top:0;background:var(--head);font-weight:500;z-index:1}.ff-ui tr:hover td{background:var(--head)}.ff-ui td:last-child,.ff-ui th:last-child{border-right:0}.ff-ui #downloads{display:flex;gap:10px;flex-wrap:wrap;margin-top:16px}.ff-ui fieldset{border:1px solid var(--line);border-radius:8px;padding:14px;margin:12px 0;min-width:0}.ff-ui legend{font-weight:600}.ff-ui label{overflow-wrap:anywhere}.ff-ui .history-item{padding:12px 0;border-bottom:1px solid var(--line)}.ff-ui .ff-field-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;background:var(--page);padding:12px;border-radius:6px}.ff-ui .ff-field-grid label{display:flex;align-items:center;gap:8px;min-height:36px}.ff-ui .ff-wizard-step{display:none}.ff-ui .ff-wizard-step[aria-current=true]{display:block}
@@ -20,3 +26,16 @@
20
26
  .ff-ui .ff-stage-icon{display:grid;place-items:center;width:30px;height:30px;border:1px solid var(--line);border-radius:7px;background:var(--surface)}.ff-ui .ff-stage-icon .ff-icon{width:17px;height:17px}
21
27
  .ff-ui .ff-stage-label{display:block;max-width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:1.35}.ff-ui .ff-stages button:focus-visible{outline-offset:-3px}
22
28
  @media(max-width:420px){.ff-ui .ff-stages button{min-height:70px;padding:8px 2px;gap:5px}.ff-ui .ff-stage-icon{width:26px;height:26px}.ff-ui .ff-stage-icon .ff-icon{width:16px;height:16px}.ff-ui .ff-stage-label{font-size:11px}}
29
+ .ff-ui .ff-shortcuts{flex-wrap:nowrap;justify-content:safe center;align-items:center;gap:8px;width:100%;max-width:var(--dsh-composer-card-max-width,780px);margin:8px auto 0;padding:4px 16px;overflow-x:auto;scrollbar-width:thin}
30
+ .ff-ui .ff-shortcuts button{flex:0 0 auto;min-width:108px;min-height:54px;padding:7px 12px;gap:5px;border:1px solid var(--line);border-radius:8px;background:var(--surface);color:var(--muted);font-size:12px}
31
+ .ff-ui .ff-shortcuts button .ff-icon{width:17px;height:17px}.ff-ui .ff-shortcut-label{white-space:nowrap}
32
+ .ff-ui .ff-shortcuts button:hover:not(:disabled),.ff-ui .ff-shortcuts button:focus-visible{background:var(--soft);border-color:var(--line);color:var(--action)}
33
+ [data-form-fill-mount]{width:100%;min-width:0;max-width:100%}[data-form-fill-mount]>.ff-ui{min-width:0;max-width:100%}
34
+ .ff-ui.ff-workbench{padding:0;background:var(--page);min-width:0}.ff-ui .ff-workbench-head{padding:20px;background:var(--surface);border-bottom:1px solid var(--line)}.ff-ui .ff-workbench-head h1{font-size:18px}.ff-ui .ff-workbench-head p{margin:8px 0 0}.ff-ui.ff-embedded .ff-workbench-head{display:none}
35
+ .ff-ui .ff-panel-head{padding:16px;background:var(--surface)}.ff-ui .ff-panel-actions{flex-shrink:0}.ff-ui .ff-panel-actions button{font-size:12px;padding:6px 8px}.ff-ui .ff-panel-head h2{font-size:16px;white-space:nowrap}
36
+ .ff-ui .task-tabs{padding:0 20px;background:var(--surface)}.ff-ui #status{margin:0;padding:10px 20px;background:var(--head);overflow-wrap:anywhere;min-height:0;border-bottom:1px solid var(--line)}.ff-ui #status:empty,.ff-ui #task-meta:empty{display:none}
37
+ .ff-ui #current,.ff-ui #preview{min-width:0}.ff-ui #current:not(:has(#preview:not([hidden]))) .ff-execution-toolbar{display:none}
38
+ .ff-ui #task-meta{margin:16px 20px 0;max-width:calc(100% - 40px);white-space:normal;overflow-wrap:anywhere}.ff-ui .ff-execution-toolbar{margin:12px 20px;justify-content:flex-start}
39
+ .ff-ui .card{margin:16px 20px;padding:20px;border:1px solid var(--line);border-radius:8px;background:var(--surface);min-width:0}.ff-ui .card h2{margin:0 0 12px;font-size:18px}.ff-ui .card p{margin:8px 0 16px}.ff-ui #grid{margin:16px 20px;padding:16px;background:var(--surface);border:1px solid var(--line);border-radius:8px;min-width:0}.ff-ui #grid summary{cursor:pointer;font-weight:600}
40
+ .ff-ui #history{padding:20px}.ff-ui #preview>.controls{padding:0 20px 20px}.ff-ui input[type=file]{width:100%;max-width:100%;min-width:0;font-size:12px}.ff-ui input[type=file]::file-selector-button{font:inherit;border:1px solid var(--line);border-radius:6px;padding:8px 12px;margin-right:10px;background:var(--surface);color:var(--text);cursor:pointer}.ff-ui #samples{margin-top:12px}.ff-ui .history-item{background:var(--surface);padding:16px;border:1px solid var(--line);border-radius:8px;margin-top:12px}
41
+ @media(max-width:640px){.ff-ui .ff-shortcuts{justify-content:flex-start;padding-inline:12px;gap:8px}.ff-ui .ff-shortcuts button{flex:0 0 auto;min-width:92px;padding:7px 10px;font-size:12px}.ff-ui .card{margin:14px;padding:16px}.ff-ui #grid{margin:14px}.ff-ui .task-tabs{padding-inline:14px}.ff-ui #status{padding-inline:14px}.ff-ui #task-meta{margin-inline:14px;max-width:calc(100% - 28px)}.ff-ui .ff-execution-toolbar{margin-inline:14px}.ff-ui .ff-panel-head{padding:12px}.ff-ui .ff-panel-head .ff-brand{gap:8px}.ff-ui .ff-panel-head h2{font-size:14px}.ff-ui .ff-panel-actions{gap:4px}.ff-ui .ff-panel-actions button{font-size:11px;padding:6px}.ff-ui .ff-workbench-head{padding:16px}}
package/lib/client.js CHANGED
@@ -95,17 +95,40 @@ window.__ModuleLoader__.load({
95
95
  if(!owned(sessionId))return null;
96
96
  const content=h('div',{className:'ff-ui','data-ff-theme':theme()},h('nav',{className:'ff-shortcuts','aria-label':'AI填表快捷菜单'},...[
97
97
  ['导入表格','import','upload'],['字段设置','rules','check'],['主体核验','identity','search'],['填写预览','preview','table'],['任务历史','history','history']
98
- ].map(([label,step,symbol])=>h('button',{key:step,type:'button',onClick:()=>openPanel(sessionId,step)},icon(symbol),label))));
98
+ ].map(([label,step,symbol])=>h('button',{key:step,type:'button',onClick:()=>openPanel(sessionId,step)},icon(symbol),h('span',{className:'ff-shortcut-label'},label)))));
99
99
  return h('div',{ref,'data-form-fill-session':sessionId},h(Hero,{sessionId}),mount?portal(content,mount):content);
100
100
  }
101
101
  function Panel(){
102
- const [view,setView]=React.useState(null),frame=React.useRef(null),[active,setActive]=React.useState(current()),[wide,setWide]=React.useState(innerWidth>=1100),[expanded,setExpanded]=React.useState(false),[modal,setModal]=React.useState(false);
102
+ const [view,setView]=React.useState(null),frame=React.useRef(null),[active,setActive]=React.useState(current()),[available,setAvailable]=React.useState(innerWidth),[expanded,setExpanded]=React.useState(false),[modal,setModal]=React.useState(false);
103
+ const [preferred,setPreferred]=React.useState(null),[dragging,setDragging]=React.useState(false),drag=React.useRef(null);
104
+ const wide=available>=740&&innerWidth>760,maxWidth=Math.max(320,available-420);
105
+ const clamp=value=>Math.round(Math.max(320,Math.min(maxWidth,value)));
106
+ const panelWidth=clamp(expanded?maxWidth:preferred??Math.min(640,available*.48));
107
+ const finishDrag=(cancel=false)=>{
108
+ const prior=drag.current;if(!prior)return;drag.current=null;setDragging(false);
109
+ if(cancel){setPreferred(prior.preferred);setExpanded(prior.expanded)}
110
+ if(prior.node.hasPointerCapture(prior.id))prior.node.releasePointerCapture(prior.id);
111
+ };
112
+ const resizeKeys=event=>{
113
+ if(event.key==='Escape'&&drag.current){event.preventDefault();event.stopPropagation();finishDrag(true);return}
114
+ if(drag.current)return;
115
+ const amount=event.shiftKey?48:16,value={ArrowLeft:panelWidth+amount,ArrowRight:panelWidth-amount,Home:320,End:maxWidth}[event.key];
116
+ if(value===undefined)return;event.preventDefault();setExpanded(false);setPreferred(clamp(value));
117
+ };
103
118
  React.useEffect(()=>{
104
119
  const sync=()=>{const value=theme();document.querySelectorAll('.ff-ui').forEach(n=>n.dataset.ffTheme=value);frame.current?.contentWindow?.postMessage({type:'ff-theme',theme:value},location.origin)};
105
120
  const observer=new MutationObserver(sync);observer.observe(document.documentElement,{attributes:true,attributeFilter:['class','style','data-ds-dark-theme']});const media=matchMedia('(prefers-color-scheme:dark)');media.addEventListener('change',sync);sync();
106
121
  return()=>{observer.disconnect();media.removeEventListener('change',sync)};
107
122
  },[]);
108
- React.useEffect(()=>{const resize=()=>setWide(innerWidth>=1100);window.addEventListener('resize',resize);return()=>window.removeEventListener('resize',resize)},[]);
123
+ React.useEffect(()=>{
124
+ const measure=()=>{const container=document.querySelector('[data-composer-card]')?.closest('[data-phase]')||document.querySelector('[data-slot="conversation"]');setAvailable(Math.max(0,innerWidth-Math.max(0,container?.getBoundingClientRect().left||0)))};
125
+ const resize=()=>{finishDrag(true);measure()},blur=()=>finishDrag(true);
126
+ const escape=event=>{if(event.key==='Escape'&&drag.current){event.preventDefault();event.stopImmediatePropagation();finishDrag(true)}};
127
+ const observer=new ResizeObserver(measure);observer.observe(document.body);
128
+ const container=document.querySelector('[data-composer-card]')?.closest('[data-phase]')||document.querySelector('[data-slot="conversation"]');if(container)observer.observe(container);
129
+ measure();window.addEventListener('resize',resize);window.addEventListener('blur',blur);window.addEventListener('keydown',escape,true);
130
+ return()=>{finishDrag(true);observer.disconnect();window.removeEventListener('resize',resize);window.removeEventListener('blur',blur);window.removeEventListener('keydown',escape,true)};
131
+ },[view?.id,active,modal]);
109
132
  React.useEffect(()=>{panel=setView;const timer=setInterval(()=>setActive(current()),200);return()=>{panel=null;clearInterval(timer)}},[]);
110
133
  React.useEffect(()=>{if(view&&current()!==view.id)setView(null);setModal(false)},[active,view?.id]);
111
134
  React.useEffect(()=>{frame.current?.contentWindow?.postMessage({type:'ff-navigate',step:view?.step},location.origin)},[view]);
@@ -140,7 +163,7 @@ window.__ModuleLoader__.load({
140
163
  window.addEventListener('message',receive);return()=>window.removeEventListener('message',receive);
141
164
  },[view?.id]);
142
165
  React.useEffect(()=>{
143
- if(!view||active!==view.id)return;
166
+ if(!view||active!==view.id||!wide||modal)return;
144
167
  const touched=new Map();
145
168
  const adjust=()=>{
146
169
  if(current()!==view.id)return;
@@ -150,14 +173,20 @@ window.__ModuleLoader__.load({
150
173
  if(!container)return;
151
174
  if(!touched.has(container))touched.set(container,container.style.getPropertyValue('--ff-panel-inset'));
152
175
  container.setAttribute('data-form-fill-reserve','true');
153
- container.style.setProperty('--ff-panel-inset',expanded?'calc(min(58vw, 860px) + 1px)':'calc(min(48vw, 640px) + 1px)');
176
+ container.style.setProperty('--ff-panel-inset',panelWidth+'px');
154
177
  };
155
178
  adjust();const observer=new MutationObserver(adjust);observer.observe(document.body,{childList:true,subtree:true});
156
179
  return()=>{observer.disconnect();for(const [node,previous]of touched){node.removeAttribute('data-form-fill-reserve');if(previous)node.style.setProperty('--ff-panel-inset',previous);else node.style.removeProperty('--ff-panel-inset')}};
157
- },[view?.id,expanded,active]);
180
+ },[view?.id,panelWidth,active,wide,modal]);
158
181
  if(!view||active!==view.id)return null;
159
- return portal(h('section',{className:'ff-ui ff-panel','data-ff-theme':theme(),'aria-label':'AI填表工作台',style:{width:modal?'100%':wide?(expanded?'min(58vw, 860px)':'min(48vw, 640px)'):'100%',...(modal?{background:'transparent',border:0}:{})}},
160
- h('div',{className:'ff-panel-head',hidden:modal},h('div',{className:'ff-brand'},mark(),h('h2',null,'AI填表')),h('div',{className:'ff-panel-actions'},h('button',{onClick:()=>setExpanded(v=>!v)},expanded?'收起展开':'展开工作台'),h('button',{onClick:()=>setView(null)},'关闭工作台'))),
182
+ return portal(h('section',{className:'ff-ui ff-panel'+(dragging?' ff-resizing':''),'data-ff-theme':theme(),'aria-label':'AI填表工作台',style:{width:modal?'100%':wide?panelWidth+'px':'100%',...(modal?{background:'transparent',border:0}:{})}},
183
+ wide&&!modal?h('div',{className:'ff-panel-resize',role:'separator',tabIndex:0,'aria-label':'调整工作台宽度','aria-orientation':'vertical','aria-valuemin':320,'aria-valuemax':maxWidth,'aria-valuenow':panelWidth,'aria-valuetext':panelWidth+' 像素',title:'拖动调宽;左右键微调,Home/End 最小/最大;双击恢复默认',onKeyDown:resizeKeys,
184
+ onDoubleClick:()=>{finishDrag(true);setPreferred(null);setExpanded(false)},
185
+ onPointerDown:event=>{if(event.button!==0||drag.current)return;event.preventDefault();event.currentTarget.focus();drag.current={id:event.pointerId,node:event.currentTarget,x:event.clientX,width:panelWidth,preferred,expanded};event.currentTarget.setPointerCapture(event.pointerId);setDragging(true)},
186
+ onPointerMove:event=>{const prior=drag.current;if(prior?.id!==event.pointerId)return;setPreferred(clamp(prior.width+prior.x-event.clientX));setExpanded(false)},
187
+ onPointerUp:()=>finishDrag(),onPointerCancel:()=>finishDrag(true),onLostPointerCapture:()=>finishDrag(true)
188
+ }):null,
189
+ h('div',{className:'ff-panel-head',hidden:modal},h('div',{className:'ff-brand'},mark(),h('h2',null,'AI填表工作台')),h('div',{className:'ff-panel-actions'},h('button',{onClick:()=>setExpanded(v=>!v)},expanded?'收起展开':'展开工作台'),h('button',{onClick:()=>setView(null)},'关闭工作台'))),
161
190
  h('iframe',{ref:frame,title:'AI填表任务',src:'/form-fill/?session='+encodeURIComponent(view.id)+(sessionTasks.has(view.id)?'#task='+sessionTasks.get(view.id):''),onLoad:()=>{frame.current.contentWindow.postMessage({type:'ff-theme',theme:theme()},location.origin);frame.current.contentWindow.postMessage({type:'ff-navigate',step:view.step},location.origin)}})),document.body);
162
191
  }
163
192
  register('conversation.input.dock',Menu);
package/lib/ui.html CHANGED
@@ -3,13 +3,13 @@
3
3
  <title>AI填表工作台</title>
4
4
  <link id="ff-style" rel="stylesheet">
5
5
  <body class="ff-ui ff-workbench">
6
- <div class="ff-brand"><span class="ff-mark" data-icon="table"></span><h1>AI填表智能体</h1></div><p class="muted">识别空位,核验来源,确认后生成新副本。</p><nav class="task-tabs" aria-label="任务视图"><button id="current-tab" aria-pressed="true">当前任务</button><button id="history-tab" aria-pressed="false">任务历史</button></nav>
6
+ <header class="ff-workbench-head"><div class="ff-brand"><span class="ff-mark" data-icon="table"></span><h1>AI填表工作台</h1></div><p class="muted">识别空位,核验来源,确认后生成新副本。</p></header><nav class="task-tabs" aria-label="任务视图"><button id="current-tab" aria-pressed="true">当前任务</button><button id="history-tab" aria-pressed="false">任务历史</button></nav>
7
7
  <p id="status" role="status" aria-live="polite"></p>
8
8
  <section id="history" hidden><h2>本浏览器的任务</h2><p class="muted">只显示持有相同访问凭据的本地任务。过期任务自动清理。</p><div id="history-list"></div></section>
9
9
  <main id="current">
10
10
  <nav class="ff-stages" aria-label="工作流程"><button data-step="import"><span class="ff-stage-icon" data-icon="upload"></span><span class="ff-stage-label">导入表格</span></button><button data-step="rules"><span class="ff-stage-icon" data-icon="settings"></span><span class="ff-stage-label">字段设置</span></button><button data-step="identity"><span class="ff-stage-icon" data-icon="identity"></span><span class="ff-stage-label">主体核验</span></button><button data-step="preview"><span class="ff-stage-icon" data-icon="table"></span><span class="ff-stage-label">填写预览</span></button><button data-step="download"><span class="ff-stage-icon" data-icon="download"></span><span class="ff-stage-label">确认下载</span></button></nav>
11
11
  <p id="task-meta" class="badge"></p>
12
- <div class="toolbar"><span id="execution-progress" role="status"></span><button id="cancel-run" disabled>取消查询</button><button id="retry-run" disabled>仅重试失败 / 未完成查询</button></div>
12
+ <div class="toolbar ff-execution-toolbar"><span id="execution-progress" role="status"></span><button id="cancel-run" disabled>取消查询</button><button id="retry-run" disabled>仅重试失败 / 未完成查询</button></div>
13
13
  <details id="grid" hidden><summary>查看完整原表 / 结果表格</summary><p class="muted">全量行列按页查看,隐藏行列只读标注。结果视图展示当前已选填写;下载不受分页或搜索影响。</p><div class="toolbar"><label>工作表 <select id="grid-sheet"></select></label><label>视图 <select id="grid-view"><option value="original">原表</option><option value="result">结果预览</option></select></label><label>搜索全部行 <input id="grid-query" type="search" maxlength="200"></label><button id="grid-search">搜索</button></div><p id="grid-status" role="status"></p><div class="scroll"><table><thead id="grid-head"></thead><tbody id="grid-body"></tbody></table></div><div class="toolbar"><button id="grid-prev">上一页</button><button id="grid-next">下一页</button><button id="grid-left">前 16 列</button><button id="grid-right">后 16 列</button></div></details>
14
14
  <div class="card" data-pane="import"><h2>导入表格</h2><p class="muted">支持 XLSX,最大 8 MiB。支持普通内部公式、固定文本或区域下拉、基础条件格式及普通表格对象;宏、图片、图表及复杂扩展结构暂不支持。上传仅做本地分析,不查询企查查。</p>
15
15
  <div class="controls"><input type="file" id="file" accept=".xlsx" aria-label="选择 XLSX 文件"><button id="upload">分析表格</button></div>
@@ -37,6 +37,7 @@
37
37
  const basePath=location.pathname.startsWith('/form-fill')?'/form-fill':'';
38
38
  document.getElementById('ff-style').href=basePath+'/brand.css';
39
39
  const stageIcons={table:'M3 4h18v16H3zM3 9h18M9 9v11',upload:'M12 16V3m-4 4 4-4 4 4M4 14v6h16v-6',settings:'M4 6h16M4 12h16M4 18h16M8 3v6M16 9v6M10 15v6',identity:'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8m7 1 2 2 4-4',download:'M12 3v13m-4-4 4 4 4-4M4 17v4h16v-4'};
40
+ document.body.classList.toggle('ff-embedded',parent!==window);
40
41
  document.querySelectorAll('[data-icon]').forEach(node=>{const d=stageIcons[node.dataset.icon];if(!d)return;const svg=document.createElementNS('http://www.w3.org/2000/svg','svg');svg.setAttribute('viewBox','0 0 24 24');svg.setAttribute('class','ff-icon');svg.setAttribute('aria-hidden','true');const path=document.createElementNS(svg.namespaceURI,'path');path.setAttribute('d',d);svg.append(path);node.append(svg)});
41
42
  const $=id=>document.getElementById(id);
42
43
  let owner=localStorage.getItem('ff-owner');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-form-fill-agent",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "description": "AI填表智能体:XLSX blank-cell preview, QCC enrichment and persistent tasks for DSH.",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/duhu2000/dsh-form-fill-agent.git", "directory": "packages/dsh-form-fill-agent" },