@ycodium-ai/plugin-tool-display 0.1.0
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 +39 -0
- package/assets/ui.js +259 -0
- package/dist/index.js +12 -0
- package/dist/models.d.ts +96 -0
- package/dist/models.js +310 -0
- package/locales/en.json +14 -0
- package/locales/zh-CN.json +14 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @ycodium-ai/plugin-tool-display
|
|
2
|
+
|
|
3
|
+
Renders command, file-read, file-change, search, and web tool calls in a
|
|
4
|
+
Ycodium thread from facts the host already has. It does not fetch tool
|
|
5
|
+
output, open a socket, or keep a cache.
|
|
6
|
+
|
|
7
|
+
Typed overviews stay inside this package. The host keeps generic
|
|
8
|
+
overview / input / output tabs and the recorded text, so disabling or
|
|
9
|
+
uninstalling the plugin still leaves the original input and output
|
|
10
|
+
readable. Unknown formats stay as recorded text; the plugin never infers
|
|
11
|
+
success or invents fields.
|
|
12
|
+
|
|
13
|
+
Web UI lives in this package. Pure interpretation lives in `src/models.ts`
|
|
14
|
+
(no DOM) and ships as `@ycodium-ai/plugin-tool-display/models` with
|
|
15
|
+
`dist/models.js` and `dist/models.d.ts`. Mobile keeps its native fallback
|
|
16
|
+
and must not load the Web views.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
ycodium plugin add @ycodium-ai/plugin-tool-display
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The plugin ships enabled with Ycodium as the `core.tool-display` bundle
|
|
25
|
+
entry. Disabling it unregisters the typed overviews. Uninstalling writes
|
|
26
|
+
`{ id: "core.tool-display", removed: true }` so a restart does not bring
|
|
27
|
+
it back. Reinstalling the unversioned bundled package restores the same
|
|
28
|
+
entry without an npm fetch.
|
|
29
|
+
|
|
30
|
+
## Developing
|
|
31
|
+
|
|
32
|
+
Built from the Ycodium repository. The server entry bundles the plugin's
|
|
33
|
+
code; `@ycodium-ai/plugin-api` stays external and resolves to the host's
|
|
34
|
+
shared module. Types are erased at build time. The plugin declares API
|
|
35
|
+
version 2.
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
UNLICENSED.
|
package/assets/ui.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
var G=`/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties;
|
|
3
|
+
@scope (.ycodium-tool-display-ui) {
|
|
4
|
+
.m-1 {
|
|
5
|
+
margin: calc(var(--spacing, 0.25rem) * 1);
|
|
6
|
+
}
|
|
7
|
+
.mb-2 {
|
|
8
|
+
margin-bottom: calc(var(--spacing, 0.25rem) * 2);
|
|
9
|
+
}
|
|
10
|
+
.line-clamp-3 {
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
display: -webkit-box;
|
|
13
|
+
-webkit-box-orient: vertical;
|
|
14
|
+
-webkit-line-clamp: 3;
|
|
15
|
+
}
|
|
16
|
+
.flex {
|
|
17
|
+
display: flex;
|
|
18
|
+
}
|
|
19
|
+
.grid {
|
|
20
|
+
display: grid;
|
|
21
|
+
}
|
|
22
|
+
.inline-flex {
|
|
23
|
+
display: inline-flex;
|
|
24
|
+
}
|
|
25
|
+
.size-4 {
|
|
26
|
+
width: calc(var(--spacing, 0.25rem) * 4);
|
|
27
|
+
height: calc(var(--spacing, 0.25rem) * 4);
|
|
28
|
+
}
|
|
29
|
+
.size-icon-1 {
|
|
30
|
+
width: var(--control-icon-1);
|
|
31
|
+
height: var(--control-icon-1);
|
|
32
|
+
}
|
|
33
|
+
.min-h-8 {
|
|
34
|
+
min-height: calc(var(--spacing, 0.25rem) * 8);
|
|
35
|
+
}
|
|
36
|
+
.max-w-lg {
|
|
37
|
+
max-width: var(--container-lg, 32rem);
|
|
38
|
+
}
|
|
39
|
+
.min-w-0 {
|
|
40
|
+
min-width: calc(var(--spacing, 0.25rem) * 0);
|
|
41
|
+
}
|
|
42
|
+
.flex-1 {
|
|
43
|
+
flex: 1;
|
|
44
|
+
}
|
|
45
|
+
.shrink-0 {
|
|
46
|
+
flex-shrink: 0;
|
|
47
|
+
}
|
|
48
|
+
.cursor-text {
|
|
49
|
+
cursor: text;
|
|
50
|
+
}
|
|
51
|
+
.grid-cols-\\[3\\.5em_minmax\\(0\\,1fr\\)\\] {
|
|
52
|
+
grid-template-columns: 3.5em minmax(0,1fr);
|
|
53
|
+
}
|
|
54
|
+
.items-center {
|
|
55
|
+
align-items: center;
|
|
56
|
+
}
|
|
57
|
+
.gap-1 {
|
|
58
|
+
gap: calc(var(--spacing, 0.25rem) * 1);
|
|
59
|
+
}
|
|
60
|
+
.gap-2 {
|
|
61
|
+
gap: calc(var(--spacing, 0.25rem) * 2);
|
|
62
|
+
}
|
|
63
|
+
.space-y-0\\.5 {
|
|
64
|
+
:where(& > :not(:last-child)) {
|
|
65
|
+
--tw-space-y-reverse: 0;
|
|
66
|
+
margin-block-start: calc(calc(var(--spacing, 0.25rem) * 0.5) * var(--tw-space-y-reverse));
|
|
67
|
+
margin-block-end: calc(calc(var(--spacing, 0.25rem) * 0.5) * calc(1 - var(--tw-space-y-reverse)));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
.space-y-2 {
|
|
71
|
+
:where(& > :not(:last-child)) {
|
|
72
|
+
--tw-space-y-reverse: 0;
|
|
73
|
+
margin-block-start: calc(calc(var(--spacing, 0.25rem) * 2) * var(--tw-space-y-reverse));
|
|
74
|
+
margin-block-end: calc(calc(var(--spacing, 0.25rem) * 2) * calc(1 - var(--tw-space-y-reverse)));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
.truncate {
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
text-overflow: ellipsis;
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
}
|
|
82
|
+
.rounded-md {
|
|
83
|
+
border-radius: calc(var(--radius) - 2px);
|
|
84
|
+
}
|
|
85
|
+
.bg-background\\/60 {
|
|
86
|
+
background-color: var(--background);
|
|
87
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
88
|
+
background-color: color-mix(in oklab, var(--background) 60%, transparent);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
.bg-destructive\\/5 {
|
|
92
|
+
background-color: var(--destructive);
|
|
93
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
94
|
+
background-color: color-mix(in oklab, var(--destructive) 5%, transparent);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
.bg-success\\/5 {
|
|
98
|
+
background-color: var(--success);
|
|
99
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
100
|
+
background-color: color-mix(in oklab, var(--success) 5%, transparent);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
.p-3 {
|
|
104
|
+
padding: calc(var(--spacing, 0.25rem) * 3);
|
|
105
|
+
}
|
|
106
|
+
.px-2 {
|
|
107
|
+
padding-inline: calc(var(--spacing, 0.25rem) * 2);
|
|
108
|
+
}
|
|
109
|
+
.py-1 {
|
|
110
|
+
padding-block: calc(var(--spacing, 0.25rem) * 1);
|
|
111
|
+
}
|
|
112
|
+
.ps-\\[3\\.5em\\] {
|
|
113
|
+
padding-inline-start: 3.5em;
|
|
114
|
+
}
|
|
115
|
+
.pe-2 {
|
|
116
|
+
padding-inline-end: calc(var(--spacing, 0.25rem) * 2);
|
|
117
|
+
}
|
|
118
|
+
.pe-3 {
|
|
119
|
+
padding-inline-end: calc(var(--spacing, 0.25rem) * 3);
|
|
120
|
+
}
|
|
121
|
+
.pb-2 {
|
|
122
|
+
padding-bottom: calc(var(--spacing, 0.25rem) * 2);
|
|
123
|
+
}
|
|
124
|
+
.text-end {
|
|
125
|
+
text-align: end;
|
|
126
|
+
}
|
|
127
|
+
.text-sm {
|
|
128
|
+
font-size: var(--text-sm, 0.875rem);
|
|
129
|
+
line-height: var(--tw-leading, var(--text-sm--line-height, calc(1.25 / 0.875)));
|
|
130
|
+
}
|
|
131
|
+
.font-medium {
|
|
132
|
+
--tw-font-weight: var(--font-weight-medium, 500);
|
|
133
|
+
font-weight: var(--font-weight-medium, 500);
|
|
134
|
+
}
|
|
135
|
+
.\\[overflow-wrap\\:anywhere\\] {
|
|
136
|
+
overflow-wrap: anywhere;
|
|
137
|
+
}
|
|
138
|
+
.break-words {
|
|
139
|
+
overflow-wrap: break-word;
|
|
140
|
+
}
|
|
141
|
+
.break-all {
|
|
142
|
+
word-break: break-all;
|
|
143
|
+
}
|
|
144
|
+
.whitespace-pre-wrap {
|
|
145
|
+
white-space: pre-wrap;
|
|
146
|
+
}
|
|
147
|
+
.text-destructive {
|
|
148
|
+
color: var(--destructive);
|
|
149
|
+
}
|
|
150
|
+
.text-destructive-foreground {
|
|
151
|
+
color: var(--destructive-foreground);
|
|
152
|
+
}
|
|
153
|
+
.text-foreground {
|
|
154
|
+
color: var(--contrast-foreground);
|
|
155
|
+
}
|
|
156
|
+
.text-muted-foreground {
|
|
157
|
+
color: var(--contrast-muted-foreground);
|
|
158
|
+
}
|
|
159
|
+
.text-success-foreground {
|
|
160
|
+
color: var(--success-foreground);
|
|
161
|
+
}
|
|
162
|
+
.tabular-nums {
|
|
163
|
+
--tw-numeric-spacing: tabular-nums;
|
|
164
|
+
font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
|
|
165
|
+
}
|
|
166
|
+
.select-none {
|
|
167
|
+
-webkit-user-select: none;
|
|
168
|
+
user-select: none;
|
|
169
|
+
}
|
|
170
|
+
.select-text {
|
|
171
|
+
-webkit-user-select: text;
|
|
172
|
+
user-select: text;
|
|
173
|
+
}
|
|
174
|
+
.last\\:mb-0 {
|
|
175
|
+
&:last-child {
|
|
176
|
+
margin-bottom: calc(var(--spacing, 0.25rem) * 0);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
.\\[\\&_p\\]\\:my-0 {
|
|
180
|
+
& p {
|
|
181
|
+
margin-block: calc(var(--spacing, 0.25rem) * 0);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
.tool-body {
|
|
185
|
+
font-family: var(--font-mono);
|
|
186
|
+
font-size: var(--chat-detail-font-size, 13px);
|
|
187
|
+
line-height: 1.55;
|
|
188
|
+
}
|
|
189
|
+
.tool-meta {
|
|
190
|
+
font-size: var(--chat-meta-font-size, 11px);
|
|
191
|
+
font-variant-numeric: tabular-nums;
|
|
192
|
+
line-height: 1.45;
|
|
193
|
+
}
|
|
194
|
+
.tool-process-text {
|
|
195
|
+
font-size: var(--chat-process-font-size, 12px);
|
|
196
|
+
line-height: 1.5;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
@property --tw-space-y-reverse {
|
|
200
|
+
syntax: "*";
|
|
201
|
+
inherits: false;
|
|
202
|
+
initial-value: 0;
|
|
203
|
+
}
|
|
204
|
+
@property --tw-font-weight {
|
|
205
|
+
syntax: "*";
|
|
206
|
+
inherits: false;
|
|
207
|
+
}
|
|
208
|
+
@property --tw-ordinal {
|
|
209
|
+
syntax: "*";
|
|
210
|
+
inherits: false;
|
|
211
|
+
}
|
|
212
|
+
@property --tw-slashed-zero {
|
|
213
|
+
syntax: "*";
|
|
214
|
+
inherits: false;
|
|
215
|
+
}
|
|
216
|
+
@property --tw-numeric-figure {
|
|
217
|
+
syntax: "*";
|
|
218
|
+
inherits: false;
|
|
219
|
+
}
|
|
220
|
+
@property --tw-numeric-spacing {
|
|
221
|
+
syntax: "*";
|
|
222
|
+
inherits: false;
|
|
223
|
+
}
|
|
224
|
+
@property --tw-numeric-fraction {
|
|
225
|
+
syntax: "*";
|
|
226
|
+
inherits: false;
|
|
227
|
+
}
|
|
228
|
+
@layer properties {
|
|
229
|
+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
|
230
|
+
*, ::before, ::after, ::backdrop {
|
|
231
|
+
--tw-space-y-reverse: 0;
|
|
232
|
+
--tw-font-weight: initial;
|
|
233
|
+
--tw-ordinal: initial;
|
|
234
|
+
--tw-slashed-zero: initial;
|
|
235
|
+
--tw-numeric-figure: initial;
|
|
236
|
+
--tw-numeric-spacing: initial;
|
|
237
|
+
--tw-numeric-fraction: initial;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
`;var Le=window.__ycodiumPluginHost.react;var{Activity:We,Children:Ve,Component:ze,Fragment:Ne,Profiler:Xe,PureComponent:Ke,StrictMode:Ze,Suspense:_e,__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE:Qe,__COMPILER_RUNTIME:Je,act:$e,cache:je,cacheSignal:Ye,captureOwnerStack:ea,cloneElement:aa,createContext:E,createElement:h,createRef:ta,forwardRef:b,isValidElement:oa,lazy:ua,memo:la,startTransition:da,unstable_useCacheRefresh:ra,use:sa,useActionState:fa,useCallback:ia,useContext:W,useDebugValue:na,useDeferredValue:ca,useEffect:pa,useEffectEvent:ma,useId:La,useImperativeHandle:Ia,useInsertionEffect:V,useLayoutEffect:xa,useMemo:Ca,useOptimistic:ga,useReducer:ha,useRef:Sa,useState:U,useSyncExternalStore:wa,useTransition:ka,version:ya}=Le;var z=E(null),N=z.Provider;function w(){let e=W(z);if(e===null)throw new Error("Tool display UI has not been registered.");return e}var Ie=".ycodium-tool-display-ui";function xe(){let e=globalThis,a=(e.__ycodiumToolDisplayScope??0)+1;return e.__ycodiumToolDisplayScope=a,`ycodium-tool-display-ui-${a}`}function X(e,a="tool-display"){let t=xe(),o=e.replaceAll(Ie,`.${t}`),u=0,l=null;return{scopeClass:t,retain(){return typeof document>"u"?()=>{}:(u===0&&(l=document.createElement("style"),l.setAttribute("data-ycodium-plugin-css",a),l.textContent=o,document.head.appendChild(l)),u+=1,()=>{u-=1,u===0&&l!==null&&(l.remove(),l=null)})}}}function C(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)?e:null}function P(e){if(!e)return null;try{return JSON.parse(e)}catch{return null}}function Ce({entry:e,details:a}){let t=a.input?.text??e.toolIo?.input;if(!t)return null;let o=P(t);return C(o)}function Q(e){let a=C(e.entry.toolData),t=e.entry.toolLabel?.name??a?.tool??e.entry.toolTitle??e.entry.label;return typeof t=="string"?t.toLowerCase().replace(/[ _-]/g,""):""}function J(e){return e.entry.toolLifecycleStatus==="completed"&&!e.entry.displayFailed&&(e.details.exitCode===void 0||e.details.exitCode===0)}function ge(e){let a=e,t=a.match(/:(\d+)$/);if(!t?.[1])return{path:a,line:void 0,column:void 0};let o=t[1];a=a.slice(0,-t[0].length);let u=a.match(/:(\d+)$/);return u?.[1]?{path:a.slice(0,-u[0].length),line:u[1],column:o}:{path:a,line:o,column:void 0}}function K(e){return e.replaceAll("\\","/")}function Z(e){return/^\/[A-Za-z]:\//.test(e)?e.slice(1):e}function he(e){return e.replace(/[\\/]+$/,"")}function Se(e){let a=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\"));return a>=0?e.slice(a+1):e}function we(e){return e.replace(/^\.\/+/,"").replace(/^\/+/,"")}function ke(e){return e.startsWith("/")||/^[A-Za-z]:\//.test(e)}function R(e,a){let{path:t,line:o,column:u}=ge(e),l=Z(K(t)),f=l;if(a){let i=Z(K(he(a))),r=Se(i),n=l.toLowerCase(),I=i.toLowerCase(),p=`${I}/`,c=`${r.toLowerCase()}/`;if(n===I)f=r;else if(n.startsWith(p))f=`${r}/${l.slice(i.length+1)}`;else if(!ke(l)){let s=we(l);f=n.startsWith(c)?l:`${r}/${s}`}}return o?`${f}:${o}${u?`:${u}`:""}`:f}function ye(e,a){let t=e.entry.toolLabel?.path??a?.file_path;return typeof t=="string"&&t.trim()?R(t,e.workspaceRoot):null}function Pe(e){let{entry:a,details:t}=e;if(a.requestKind!=="command"&&a.itemType!=="command_execution")return null;let o=t.input?.text;return!o||t.raw?null:{kind:"terminal",command:o,output:t.output?.text??"",copyText:[o,t.output?.text].filter(u=>u!==void 0).join(`
|
|
242
|
+
|
|
243
|
+
`)}}function Ae(e){let{entry:a,details:t}=e;if(a.requestKind!=="file-read"&&a.toolTitle!=="Read File"||a.displayFailed||t.raw||t.exitCode!==void 0&&t.exitCode!==0)return null;let o=ye(e,Ce(e)),u=t.output?.text;if(!o||!u)return null;let l=[];for(let f of(u.endsWith(`
|
|
244
|
+
`)?u.slice(0,-1):u).split(`
|
|
245
|
+
`)){let i=/^\s*(\d+)→(.*)$/u.exec(f);if(!i)return null;let r=Number(i[1]);if(!Number.isSafeInteger(r)||r<1)return null;let n=l.at(-1);if(n&&r<=n.number)return null;l.push({number:r,text:i[2]})}return{kind:"read",path:o,lines:l,copyText:l.map(f=>f.text).join(`
|
|
246
|
+
`)}}function De(e){let{entry:a,details:t}=e;if(a.requestKind!=="file-change"&&a.itemType!=="file_change"||a.toolLifecycleStatus!=="completed"||a.displayFailed||t.exitCode!==void 0&&t.exitCode!==0)return null;let o=a.toolData;if(typeof o!="object"||o===null||Array.isArray(o))return null;let u=o.changes;if(!Array.isArray(u)||u.length===0)return null;let l=[];for(let[f,i]of u.entries()){if(typeof i!="object"||i===null||Array.isArray(i))return null;let{path:r,diff:n}=i;if(typeof r!="string"||!r.trim()||typeof n!="string"||!n)return null;let I=[],p=n.split(`
|
|
247
|
+
`);for(let[c,s]of p.entries()){let m=s.startsWith("+++")||s.startsWith("---")?"context":s.startsWith("+")?"added":s.startsWith("-")?"removed":"context",H=I.at(-1);H?.kind===m?H.lines.push(s):I.push({line:c,kind:m,lines:[s]})}l.push({key:`${r}:${f}`,path:R(r,e.workspaceRoot),patch:n,parts:I})}return{kind:"change",files:l,copyText:l.map(f=>`${f.path}
|
|
248
|
+
${f.patch}`).join(`
|
|
249
|
+
|
|
250
|
+
`)}}function Be(e){if(!J(e))return null;let a=Q(e);if(a!=="grep"&&a!=="glob")return null;let t=e.details.output?.text;if(!t)return null;let o=C(P(e.details.input?.text??e.entry.toolIo?.input)),u=P(t),l=C(u),f=t.trimStart().startsWith("{")||t.trimStart().startsWith("[");if(l?.isError===!0||typeof l?.error=="string")return null;let i=typeof o?.pattern=="string"?o.pattern:e.entry.toolLabel?.detail??e.entry.label;if(a==="glob"){let p=l!==null||Array.isArray(u);if(!p&&f)return null;let c=l?l.filenames??l.paths:Array.isArray(u)?u:t.trim().split(`
|
|
251
|
+
`);return!Array.isArray(c)||!c.every(s=>typeof s=="string"&&s.length>0)||!p&&!c.every(s=>/[/\\]|\.[a-z0-9]{1,12}$/i.test(s))?null:{kind:"search",resultKind:"paths",pattern:i,paths:c.map(s=>R(s,e.workspaceRoot)),copyText:t}}let r=typeof l?.content=="string"?l.content:t;if(l&&!Array.isArray(l.files)&&typeof l.content!="string"||!l&&f)return null;let n=new Map,I=(p,c,s)=>{let m=n.get(p)??[];m.push({line:c,text:s,key:`${c}:${m.length}`}),n.set(p,m)};if(Array.isArray(l?.files))for(let p of l.files){let c=C(p);if(typeof c?.path!="string"||!c.path||!Array.isArray(c.matches))return null;for(let s of c.matches){let m=C(s);if(typeof m?.lineNumber!="number"||!Number.isSafeInteger(m.lineNumber)||m.lineNumber<1||typeof m?.line!="string")return null;I(c.path,m.lineNumber,m.line)}}else for(let p of r.trimEnd().split(`
|
|
252
|
+
`)){let c=/^(.+?):([1-9]\d*):(.*)$/u.exec(p);if(!c||!Number.isSafeInteger(Number(c[2])))return null;I(c[1],Number(c[2]),c[3])}return{kind:"search",resultKind:"matches",pattern:i,files:[...n].map(([p,c])=>({key:p,path:R(p,e.workspaceRoot),matches:c})),copyText:t}}function _(e){if(typeof e!="string")return null;try{let a=new URL(e);return(a.protocol==="http:"||a.protocol==="https:")&&!a.username&&!a.password?a.href:null}catch{return null}}function Te(e){if(!J(e))return null;let a=Q(e);if(e.entry.itemType!=="web_search"&&a!=="websearch"&&a!=="webfetch"||a==="grep"||a==="glob")return null;let t=e.details.output?.text,o=P(t);if(!t)return null;let u=C(o);if(u?.isError===!0||typeof u?.error=="string")return null;let l=u?.sources??u?.results??(Array.isArray(o)?o:void 0),f=C(P(e.details.input?.text??e.entry.toolIo?.input)),i=typeof f?.query=="string"?f.query:e.entry.toolLabel?.detail??e.entry.label;if(Array.isArray(l)){let p=[];for(let c of l){let s=C(c),m=_(s?.url);if(!m||s?.title!==void 0&&typeof s.title!="string"||s?.snippet!==void 0&&typeof s.snippet!="string")return null;p.push({key:`${m}:${p.length}`,url:m,title:typeof s?.title=="string"?s.title:m,...typeof s?.snippet=="string"?{snippet:s.snippet}:{}})}return u?.answer!==void 0&&typeof u.answer!="string"?null:{kind:"web",query:i,sources:p,copyText:t,...typeof u?.answer=="string"?{answer:u.answer}:{}}}let r=_(u?.url),n=u?.statusCode;if(!r||typeof n!="number"||!Number.isInteger(n)||n<100||n>599)return null;let I=typeof u?.title=="string"?u.title:r;return{kind:"web",query:I,sources:[{key:r,url:r,title:I}],copyText:t,statusCode:n,...typeof u?.text=="string"?{answer:u.text}:{}}}var Fe=[Pe,Ae,De,Be,Te];function $(e){for(let a of Fe){let t=a(e);if(t)return t}return null}function j(e){return[e.input,e.output,e.raw].filter(a=>a!==void 0).map(a=>a.text).join(`
|
|
253
|
+
|
|
254
|
+
`)}function A(...e){return e.filter(a=>typeof a=="string"&&a.length>0).join(" ")}var v=(...e)=>e.filter((a,t,o)=>!!a&&a.trim()!==""&&o.indexOf(a)===t).join(" ").trim();var Y=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();var ee=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(a,t,o)=>o?o.toUpperCase():t.toLowerCase());var O=e=>{let a=ee(e);return a.charAt(0).toUpperCase()+a.slice(1)};var ae={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};var te=e=>{for(let a in e)if(a.startsWith("aria-")||a==="role"||a==="title")return!0;return!1};var oe=b(({color:e="currentColor",size:a=24,strokeWidth:t=2,absoluteStrokeWidth:o,className:u="",children:l,iconNode:f,...i},r)=>h("svg",{ref:r,...ae,width:a,height:a,stroke:e,strokeWidth:o?Number(t)*24/Number(a):t,className:v("lucide",u),...!l&&!te(i)&&{"aria-hidden":"true"},...i},[...f.map(([n,I])=>h(n,I)),...Array.isArray(l)?l:[l]]));var x=(e,a)=>{let t=b(({className:o,...u},l)=>h(oe,{ref:l,iconNode:a,className:v(`lucide-${Y(O(e))}`,`lucide-${e}`,o),...u}));return t.displayName=O(e),t};var Me=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],D=x("check",Me);var be=[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]],B=x("copy",be);var Re=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20",key:"13o1zl"}],["path",{d:"M2 12h20",key:"9i4pu4"}]],T=x("globe",Re);var ve=[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]],F=x("search",ve);var qe=[["path",{d:"m7 11 2-2-2-2",key:"1lz0vl"}],["path",{d:"M11 13h4",key:"1p7l4v"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2",key:"1m3agn"}]],g=x("square-terminal",qe);function k(e){return(a=>h(w().ui[e],{...a,className:`ycodium-tool-display-ui ${typeof a.className=="string"?a.className:""}`}))}var q=k("Button"),ue=k("Tooltip"),le=k("TooltipPopup"),de=k("TooltipTrigger"),re=k("Markdown"),se=k("DetailViewport");function S(){return{t:w().i18n.usePluginTranslation()}}var{jsx:d,jsxs:L,Fragment:It}=window.__ycodiumPluginHost.jsxRuntime;function fe({text:e}){let{t:a}=S(),t=w(),[o,u]=U(null),[l,f]=U(!1),i=o===e;return L("span",{className:"inline-flex items-center gap-1",children:[d(q,{type:"button",variant:"ghost-muted",size:"icon-xs","aria-label":a(i?"copied":"copy"),onKeyDown:r=>{(r.key==="Enter"||r.key===" ")&&r.stopPropagation()},onClick:()=>{Promise.resolve(t.writeClipboard(e)).then(()=>{u(e),f(!1)},()=>f(!0))},children:i?d(D,{"aria-hidden":!0,className:"size-icon-1"}):d(B,{"aria-hidden":!0,className:"size-icon-1"})}),l?d("span",{role:"status",className:"tool-meta text-destructive",children:a("copyFailed")}):null]})}var ie=new Map;function y(e){ie.set(e.kind,e)}function ne(e){return ie.get(e.kind)}function M({text:e}){return L(ue,{children:[d(de,{render:d("code",{className:"min-w-0 truncate text-foreground"}),children:e}),d(le,{className:"max-w-lg whitespace-pre-wrap break-words",children:e})]})}function Ue({model:e}){let{t:a}=S();if(e.resultKind==="paths")return e.paths.length===0?d("p",{className:"px-2 pb-2 text-muted-foreground",children:a("noSearchResults")}):d("pre",{className:"tool-body whitespace-pre-wrap break-words px-2 pb-2 text-foreground",children:e.paths.slice(0,16).join(`
|
|
255
|
+
`)});let t=16;return d("div",{className:"tool-body px-2 pb-2",children:e.files.length===0?d("p",{className:"text-muted-foreground",children:a("noSearchResults")}):e.files.map(o=>{let u=o.matches.slice(0,t);return t-=u.length,u.length===0?null:L("div",{"data-search-file":o.path,className:"mb-2 last:mb-0",children:[d("p",{className:"break-all font-medium text-foreground",children:o.path}),u.map(l=>L("div",{"data-search-line":l.line,className:"grid grid-cols-[3.5em_minmax(0,1fr)]",children:[d("span",{"aria-hidden":!0,className:"pe-3 text-end text-muted-foreground tabular-nums select-none",children:l.line}),d("span",{className:"whitespace-pre-wrap break-words text-foreground [overflow-wrap:anywhere]",children:l.text||`
|
|
256
|
+
`})]},l.key))]},o.key)})})}function Oe({model:e,context:a}){let{t}=S();return L("div",{className:"space-y-2 px-2 pb-2 text-foreground",children:[e.statusCode!==void 0?d("p",{className:A("tool-meta",e.statusCode>=400&&"text-destructive-foreground"),children:t("httpStatus",{status:e.statusCode})}):null,e.answer?d("p",{className:"tool-process-text line-clamp-3 whitespace-pre-wrap",children:e.answer}):null,e.sources.length===0?d("p",{className:"tool-process-text text-muted-foreground",children:t("noWebSources")}):e.sources.slice(0,8).map(o=>L("div",{"data-web-source":o.url,className:"space-y-0.5",children:[o.title!==e.query?d("p",{className:"tool-process-text font-medium",children:o.title}):null,d(re,{text:`<${o.url}>`,className:"tool-meta [&_p]:my-0",...a.threadRef===void 0?{}:{threadRef:a.threadRef},...a.workspaceRoot===void 0?{}:{cwd:a.workspaceRoot}}),o.snippet?d("p",{className:"tool-meta line-clamp-3 whitespace-pre-wrap text-muted-foreground",children:o.snippet}):null]},o.key))]})}y({kind:"terminal",heading:e=>L("span",{className:"flex min-w-0 items-center gap-2 text-foreground",children:[d(g,{"aria-hidden":!0,className:"size-4 shrink-0 text-muted-foreground"}),d(M,{text:e.command})]}),body:e=>d("pre",{className:"tool-body cursor-text whitespace-pre-wrap break-words px-2 pb-2 text-foreground select-text [overflow-wrap:anywhere]",children:e.output})});y({kind:"read",hiddenLines:e=>Math.max(0,e.lines.length-16),heading:e=>d(M,{text:e.path}),body:e=>d("div",{className:"tool-body cursor-text pb-2 text-foreground select-text",children:(e.lines.length>16?[...e.lines.slice(0,8),null,...e.lines.slice(-8)]:e.lines).map(a=>a===null?d("div",{"aria-hidden":!0,className:"ps-[3.5em] text-muted-foreground",children:"\u2026"},"gap"):L("div",{"data-source-line":a.number,className:"grid grid-cols-[3.5em_minmax(0,1fr)]",children:[d("span",{"aria-hidden":!0,className:"pe-3 text-end text-muted-foreground select-none tabular-nums",children:a.number}),d("span",{className:"whitespace-pre-wrap break-words pe-2 [overflow-wrap:anywhere]",children:a.text||`
|
|
257
|
+
`})]},a.number))})});y({kind:"change",heading:e=>d(M,{text:e.files.map(a=>a.path).join(", ")}),body:e=>d("div",{className:"tool-body cursor-text pb-2 select-text",children:e.files.map(a=>L("div",{children:[e.files.length>1?d("p",{className:"px-2 py-1 text-foreground",children:a.path}):null,a.parts.length>64?d("pre",{className:"whitespace-pre-wrap break-words px-2 text-foreground [overflow-wrap:anywhere]",children:a.patch}):a.parts.map(t=>d("pre",{"data-change-line":t.kind,className:A("whitespace-pre-wrap break-words px-2 [overflow-wrap:anywhere]",t.kind==="removed"?"bg-destructive/5 text-destructive-foreground":t.kind==="added"?"bg-success/5 text-success-foreground":"text-muted-foreground"),children:t.lines.join(`
|
|
258
|
+
`)},t.line))]},a.key))})});y({kind:"search",hasMoreResults:e=>e.resultKind==="paths"?e.paths.length>16:e.files.reduce((a,t)=>a+t.matches.length,0)>16,heading:e=>L("span",{className:"flex min-w-0 items-center gap-2",children:[d(F,{"aria-hidden":!0,className:"size-4 shrink-0"}),d(M,{text:e.pattern})]}),body:e=>d(Ue,{model:e})});y({kind:"web",hasMoreResults:e=>e.sources.length>8,heading:e=>L("span",{className:"flex min-w-0 items-center gap-2",children:[d(T,{"aria-hidden":!0,className:"size-4 shrink-0"}),d(M,{text:e.query})]}),body:(e,a)=>d(Oe,{model:e,context:a})});function He({text:e}){return d("pre",{className:"tool-body cursor-text whitespace-pre-wrap break-words px-2 pb-2 text-foreground select-text [overflow-wrap:anywhere]",children:e})}function ce({retain:e}){return V(()=>e(),[e]),null}function pe(e){let{t:a}=S(),t=$(e),o=t?ne(t):void 0,u=t&&o?.hiddenLines?o.hiddenLines(t):0,l=t&&o?.hasMoreResults?o.hasMoreResults(t):!1,f=[e.details.input,e.details.output,e.details.raw].filter(n=>n!==void 0),i=t?.copyText??j(e.details);if(!i&&!t)return null;let r=e.details.files.length>0&&t?.kind!=="change"&&t?.kind!=="search";return L("div",{className:A("ycodium-tool-display-ui",e.scopeClass,"min-w-0 rounded-md bg-background/60"),"data-tool-overview":!0,"data-tool-overview-kind":t?.kind??"generic",children:[L("div",{className:"tool-meta flex min-h-8 items-center gap-2 px-2 text-muted-foreground",children:[d("span",{className:"min-w-0 flex-1",children:t&&o?o.heading(t):null}),e.summary?d("span",{children:a("summary")}):null,f.some(n=>n.truncated)?d("span",{children:a("truncated")}):null,d(fe,{text:i})]}),d(se,{threadKey:e.threadKey??"unscoped",stateKey:`${e.stateKey??e.entry.id}:overview`,maxHeight:"8lh",className:"tool-body",followTail:e.followOutputTail===!0,...e.onDisclosureChange===void 0?{}:{onDisclosureChange:e.onDisclosureChange},children:t&&o?o.body(t,e):d(He,{text:i})}),u||l?d(q,{size:"xs",variant:"ghost-muted",className:"m-1",disabled:e.onShowOutput===void 0,onClick:e.onShowOutput,children:u?a("showMoreLines",{count:u}):a("showAllResults")}):null,r?d("div",{className:"min-w-0 px-2 pb-2",children:d("pre",{className:"tool-body whitespace-pre-wrap break-words text-foreground select-text",children:e.details.files.join(`
|
|
259
|
+
`)})}):null]})}function Ge(e){if(["Button","Tooltip","TooltipTrigger","TooltipPopup","Markdown","DetailViewport"].some(u=>e.ui[u]===void 0)){let u=function(){let l=e.i18n.usePluginTranslation();return d("p",{role:"status",className:"p-3 text-sm text-muted-foreground",children:l("hostUpgradeRequired")})};var o=u;return{pages:{},panels:{},views:{toolCall:u}}}let t=X(G);return{pages:{},panels:{},views:{toolCall:function(l){return L(N,{value:e,children:[d(ce,{retain:t.retain}),d(pe,{...l,scopeClass:t.scopeClass})]})}}}}export{Ge as default};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { definePlugin } from "@ycodium-ai/plugin-api";
|
|
3
|
+
import { TOOL_DISPLAY_PLUGIN_ID } from "@ycodium-ai/plugin-api/tool-display";
|
|
4
|
+
var index_default = definePlugin({
|
|
5
|
+
id: TOOL_DISPLAY_PLUGIN_ID,
|
|
6
|
+
apiVersion: 2,
|
|
7
|
+
activate: () => {
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
export {
|
|
11
|
+
index_default as default
|
|
12
|
+
};
|
package/dist/models.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 第一方工具概览解释。无 DOM,Web 呈现与 Mobile 原生 fallback 可共用。
|
|
3
|
+
* 升级本插件即可改规则,不锁在宿主 API 里。
|
|
4
|
+
*/
|
|
5
|
+
import type { ToolDisplayContext } from "@ycodium-ai/plugin-api/tool-display";
|
|
6
|
+
export interface ToolDisplayReadLine {
|
|
7
|
+
readonly number: number;
|
|
8
|
+
readonly text: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ToolDisplayChangePart {
|
|
11
|
+
readonly line: number;
|
|
12
|
+
readonly kind: "added" | "removed" | "context";
|
|
13
|
+
readonly lines: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface ToolDisplayChangeFile {
|
|
16
|
+
readonly key: string;
|
|
17
|
+
readonly path: string;
|
|
18
|
+
readonly patch: string;
|
|
19
|
+
readonly parts: ReadonlyArray<ToolDisplayChangePart>;
|
|
20
|
+
}
|
|
21
|
+
export interface ToolDisplaySearchFile {
|
|
22
|
+
readonly key: string;
|
|
23
|
+
readonly path: string;
|
|
24
|
+
readonly matches: ReadonlyArray<{
|
|
25
|
+
readonly line: number;
|
|
26
|
+
readonly text: string;
|
|
27
|
+
readonly key: string;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
export interface ToolDisplayWebSource {
|
|
31
|
+
readonly key: string;
|
|
32
|
+
readonly url: string;
|
|
33
|
+
readonly title: string;
|
|
34
|
+
readonly snippet?: string;
|
|
35
|
+
}
|
|
36
|
+
export type ToolDisplayOverview = {
|
|
37
|
+
readonly kind: "terminal";
|
|
38
|
+
readonly command: string;
|
|
39
|
+
readonly output: string;
|
|
40
|
+
readonly copyText: string;
|
|
41
|
+
} | {
|
|
42
|
+
readonly kind: "read";
|
|
43
|
+
readonly path: string;
|
|
44
|
+
readonly lines: ReadonlyArray<ToolDisplayReadLine>;
|
|
45
|
+
readonly copyText: string;
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: "change";
|
|
48
|
+
readonly files: ReadonlyArray<ToolDisplayChangeFile>;
|
|
49
|
+
readonly copyText: string;
|
|
50
|
+
} | ({
|
|
51
|
+
readonly kind: "search";
|
|
52
|
+
readonly pattern: string;
|
|
53
|
+
readonly copyText: string;
|
|
54
|
+
} & ({
|
|
55
|
+
readonly resultKind: "paths";
|
|
56
|
+
readonly paths: ReadonlyArray<string>;
|
|
57
|
+
} | {
|
|
58
|
+
readonly resultKind: "matches";
|
|
59
|
+
readonly files: ReadonlyArray<ToolDisplaySearchFile>;
|
|
60
|
+
})) | {
|
|
61
|
+
readonly kind: "web";
|
|
62
|
+
readonly query: string;
|
|
63
|
+
readonly sources: ReadonlyArray<ToolDisplayWebSource>;
|
|
64
|
+
readonly copyText: string;
|
|
65
|
+
readonly answer?: string;
|
|
66
|
+
readonly statusCode?: number;
|
|
67
|
+
};
|
|
68
|
+
export type ToolDisplayOverviewKind = ToolDisplayOverview["kind"];
|
|
69
|
+
/** 把绝对路径换成工作区根下的相对显示路径;不在根下则原样返回。无 DOM。 */
|
|
70
|
+
export declare function formatToolDisplayPath(pathWithPosition: string, workspaceRoot: string | undefined): string;
|
|
71
|
+
/** 只认明确的命令执行项;无命令原文或 MCP raw 时回退,不把任意输入猜成 shell。 */
|
|
72
|
+
export declare function commandOverviewModel(context: ToolDisplayContext): Extract<ToolDisplayOverview, {
|
|
73
|
+
kind: "terminal";
|
|
74
|
+
}> | null;
|
|
75
|
+
/** 只识别 Read 原文里的明确行号;无编号、错误说明或混合格式保持原文。 */
|
|
76
|
+
export declare function readOverviewModel(context: ToolDisplayContext): Extract<ToolDisplayOverview, {
|
|
77
|
+
kind: "read";
|
|
78
|
+
}> | null;
|
|
79
|
+
/** 只展示完成事件报告的 patch;请求参数、失败和未知状态不能证明修改落地。 */
|
|
80
|
+
export declare function changeOverviewModel(context: ToolDisplayContext): Extract<ToolDisplayOverview, {
|
|
81
|
+
kind: "change";
|
|
82
|
+
}> | null;
|
|
83
|
+
/** 只解释明确的搜索结果格式,不从查询参数猜命中文件或行号。 */
|
|
84
|
+
export declare function searchOverviewModel(context: ToolDisplayContext): Extract<ToolDisplayOverview, {
|
|
85
|
+
kind: "search";
|
|
86
|
+
}> | null;
|
|
87
|
+
/** 链接只来自返回结果;请求 URL 和搜索词不能冒充已取得的来源。 */
|
|
88
|
+
export declare function webOverviewModel(context: ToolDisplayContext): Extract<ToolDisplayOverview, {
|
|
89
|
+
kind: "web";
|
|
90
|
+
}> | null;
|
|
91
|
+
/**
|
|
92
|
+
* 按类型尝试第一方解释;都不匹配时返回 null,调用方必须显示已取得的原文。
|
|
93
|
+
* 这张表只装配解释,不创建连接或缓存。
|
|
94
|
+
*/
|
|
95
|
+
export declare function resolveToolDisplayOverview(context: ToolDisplayContext): ToolDisplayOverview | null;
|
|
96
|
+
export declare function recordedToolDisplayText(details: ToolDisplayContext["details"]): string;
|
package/dist/models.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// src/models.ts
|
|
2
|
+
function record(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
4
|
+
}
|
|
5
|
+
function parseJson(text) {
|
|
6
|
+
if (!text) return null;
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(text);
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function inputRecord({ entry, details }) {
|
|
14
|
+
const input = details.input?.text ?? entry.toolIo?.input;
|
|
15
|
+
if (!input) return null;
|
|
16
|
+
const value = parseJson(input);
|
|
17
|
+
return record(value);
|
|
18
|
+
}
|
|
19
|
+
function toolName(context) {
|
|
20
|
+
const raw = record(context.entry.toolData);
|
|
21
|
+
const value = context.entry.toolLabel?.name ?? raw?.tool ?? context.entry.toolTitle ?? context.entry.label;
|
|
22
|
+
return typeof value === "string" ? value.toLowerCase().replace(/[ _-]/g, "") : "";
|
|
23
|
+
}
|
|
24
|
+
function completed(context) {
|
|
25
|
+
return context.entry.toolLifecycleStatus === "completed" && !context.entry.displayFailed && (context.details.exitCode === void 0 || context.details.exitCode === 0);
|
|
26
|
+
}
|
|
27
|
+
function splitPathAndPosition(value) {
|
|
28
|
+
let path = value;
|
|
29
|
+
const columnMatch = path.match(/:(\d+)$/);
|
|
30
|
+
if (!columnMatch?.[1]) return { path, line: void 0, column: void 0 };
|
|
31
|
+
let column = columnMatch[1];
|
|
32
|
+
path = path.slice(0, -columnMatch[0].length);
|
|
33
|
+
const lineMatch = path.match(/:(\d+)$/);
|
|
34
|
+
if (lineMatch?.[1]) {
|
|
35
|
+
return { path: path.slice(0, -lineMatch[0].length), line: lineMatch[1], column };
|
|
36
|
+
}
|
|
37
|
+
return { path, line: column, column: void 0 };
|
|
38
|
+
}
|
|
39
|
+
function normalizePathSeparators(path) {
|
|
40
|
+
return path.replaceAll("\\", "/");
|
|
41
|
+
}
|
|
42
|
+
function canonicalizeWindowsDrivePath(path) {
|
|
43
|
+
return /^\/[A-Za-z]:\//.test(path) ? path.slice(1) : path;
|
|
44
|
+
}
|
|
45
|
+
function trimTrailingPathSeparators(path) {
|
|
46
|
+
return path.replace(/[\\/]+$/, "");
|
|
47
|
+
}
|
|
48
|
+
function basenameOfPath(path) {
|
|
49
|
+
const separatorIndex = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
50
|
+
return separatorIndex >= 0 ? path.slice(separatorIndex + 1) : path;
|
|
51
|
+
}
|
|
52
|
+
function stripRelativePrefixes(path) {
|
|
53
|
+
return path.replace(/^\.\/+/, "").replace(/^\/+/, "");
|
|
54
|
+
}
|
|
55
|
+
function isAbsolutePath(path) {
|
|
56
|
+
return path.startsWith("/") || /^[A-Za-z]:\//.test(path);
|
|
57
|
+
}
|
|
58
|
+
function formatToolDisplayPath(pathWithPosition, workspaceRoot) {
|
|
59
|
+
const { path, line, column } = splitPathAndPosition(pathWithPosition);
|
|
60
|
+
const normalizedPath = canonicalizeWindowsDrivePath(normalizePathSeparators(path));
|
|
61
|
+
let displayPath = normalizedPath;
|
|
62
|
+
if (workspaceRoot) {
|
|
63
|
+
const normalizedWorkspaceRoot = canonicalizeWindowsDrivePath(
|
|
64
|
+
normalizePathSeparators(trimTrailingPathSeparators(workspaceRoot))
|
|
65
|
+
);
|
|
66
|
+
const workspaceLabel = basenameOfPath(normalizedWorkspaceRoot);
|
|
67
|
+
const pathForCompare = normalizedPath.toLowerCase();
|
|
68
|
+
const workspaceForCompare = normalizedWorkspaceRoot.toLowerCase();
|
|
69
|
+
const workspaceWithSeparator = `${workspaceForCompare}/`;
|
|
70
|
+
const workspaceLabelWithSeparator = `${workspaceLabel.toLowerCase()}/`;
|
|
71
|
+
if (pathForCompare === workspaceForCompare) {
|
|
72
|
+
displayPath = workspaceLabel;
|
|
73
|
+
} else if (pathForCompare.startsWith(workspaceWithSeparator)) {
|
|
74
|
+
displayPath = `${workspaceLabel}/${normalizedPath.slice(normalizedWorkspaceRoot.length + 1)}`;
|
|
75
|
+
} else if (!isAbsolutePath(normalizedPath)) {
|
|
76
|
+
const relativePath = stripRelativePrefixes(normalizedPath);
|
|
77
|
+
displayPath = pathForCompare.startsWith(workspaceLabelWithSeparator) ? normalizedPath : `${workspaceLabel}/${relativePath}`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!line) return displayPath;
|
|
81
|
+
return `${displayPath}:${line}${column ? `:${column}` : ""}`;
|
|
82
|
+
}
|
|
83
|
+
function reportedPath(context, input) {
|
|
84
|
+
const value = context.entry.toolLabel?.path ?? input?.file_path;
|
|
85
|
+
return typeof value === "string" && value.trim() ? formatToolDisplayPath(value, context.workspaceRoot) : null;
|
|
86
|
+
}
|
|
87
|
+
function commandOverviewModel(context) {
|
|
88
|
+
const { entry, details } = context;
|
|
89
|
+
if (entry.requestKind !== "command" && entry.itemType !== "command_execution") return null;
|
|
90
|
+
const command = details.input?.text;
|
|
91
|
+
if (!command || details.raw) return null;
|
|
92
|
+
return {
|
|
93
|
+
kind: "terminal",
|
|
94
|
+
command,
|
|
95
|
+
output: details.output?.text ?? "",
|
|
96
|
+
copyText: [command, details.output?.text].filter((text) => text !== void 0).join("\n\n")
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function readOverviewModel(context) {
|
|
100
|
+
const { entry, details } = context;
|
|
101
|
+
if (entry.requestKind !== "file-read" && entry.toolTitle !== "Read File") return null;
|
|
102
|
+
if (entry.displayFailed || details.raw || details.exitCode !== void 0 && details.exitCode !== 0) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const path = reportedPath(context, inputRecord(context));
|
|
106
|
+
const text = details.output?.text;
|
|
107
|
+
if (!path || !text) return null;
|
|
108
|
+
const lines = [];
|
|
109
|
+
for (const raw of (text.endsWith("\n") ? text.slice(0, -1) : text).split("\n")) {
|
|
110
|
+
const match = /^\s*(\d+)→(.*)$/u.exec(raw);
|
|
111
|
+
if (!match) return null;
|
|
112
|
+
const number = Number(match[1]);
|
|
113
|
+
if (!Number.isSafeInteger(number) || number < 1) return null;
|
|
114
|
+
const previous = lines.at(-1);
|
|
115
|
+
if (previous && number <= previous.number) return null;
|
|
116
|
+
lines.push({ number, text: match[2] });
|
|
117
|
+
}
|
|
118
|
+
return { kind: "read", path, lines, copyText: lines.map((line) => line.text).join("\n") };
|
|
119
|
+
}
|
|
120
|
+
function changeOverviewModel(context) {
|
|
121
|
+
const { entry, details } = context;
|
|
122
|
+
if (entry.requestKind !== "file-change" && entry.itemType !== "file_change") return null;
|
|
123
|
+
if (entry.toolLifecycleStatus !== "completed" || entry.displayFailed || details.exitCode !== void 0 && details.exitCode !== 0)
|
|
124
|
+
return null;
|
|
125
|
+
const data = entry.toolData;
|
|
126
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) return null;
|
|
127
|
+
const changes = data.changes;
|
|
128
|
+
if (!Array.isArray(changes) || changes.length === 0) return null;
|
|
129
|
+
const files = [];
|
|
130
|
+
for (const [changeIndex, value] of changes.entries()) {
|
|
131
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
|
|
132
|
+
const { path, diff } = value;
|
|
133
|
+
if (typeof path !== "string" || !path.trim() || typeof diff !== "string" || !diff) return null;
|
|
134
|
+
const parts = [];
|
|
135
|
+
const lines = diff.split("\n");
|
|
136
|
+
for (const [index, line] of lines.entries()) {
|
|
137
|
+
const kind = line.startsWith("+++") || line.startsWith("---") ? "context" : line.startsWith("+") ? "added" : line.startsWith("-") ? "removed" : "context";
|
|
138
|
+
const previous = parts.at(-1);
|
|
139
|
+
if (previous?.kind === kind) previous.lines.push(line);
|
|
140
|
+
else parts.push({ line: index, kind, lines: [line] });
|
|
141
|
+
}
|
|
142
|
+
files.push({
|
|
143
|
+
key: `${path}:${changeIndex}`,
|
|
144
|
+
path: formatToolDisplayPath(path, context.workspaceRoot),
|
|
145
|
+
patch: diff,
|
|
146
|
+
parts
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
kind: "change",
|
|
151
|
+
files,
|
|
152
|
+
copyText: files.map((file) => `${file.path}
|
|
153
|
+
${file.patch}`).join("\n\n")
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function searchOverviewModel(context) {
|
|
157
|
+
if (!completed(context)) return null;
|
|
158
|
+
const tool = toolName(context);
|
|
159
|
+
if (tool !== "grep" && tool !== "glob") return null;
|
|
160
|
+
const output = context.details.output?.text;
|
|
161
|
+
if (!output) return null;
|
|
162
|
+
const input = record(parseJson(context.details.input?.text ?? context.entry.toolIo?.input));
|
|
163
|
+
const decoded = parseJson(output);
|
|
164
|
+
const result = record(decoded);
|
|
165
|
+
const jsonShape = output.trimStart().startsWith("{") || output.trimStart().startsWith("[");
|
|
166
|
+
if (result?.isError === true || typeof result?.error === "string") return null;
|
|
167
|
+
const pattern = typeof input?.pattern === "string" ? input.pattern : context.entry.toolLabel?.detail ?? context.entry.label;
|
|
168
|
+
if (tool === "glob") {
|
|
169
|
+
const structured = result !== null || Array.isArray(decoded);
|
|
170
|
+
if (!structured && jsonShape) return null;
|
|
171
|
+
const paths = result ? result.filenames ?? result.paths : Array.isArray(decoded) ? decoded : output.trim().split("\n");
|
|
172
|
+
if (!Array.isArray(paths) || !paths.every((path) => typeof path === "string" && path.length > 0))
|
|
173
|
+
return null;
|
|
174
|
+
if (!structured && !paths.every((path) => /[/\\]|\.[a-z0-9]{1,12}$/i.test(path))) return null;
|
|
175
|
+
return {
|
|
176
|
+
kind: "search",
|
|
177
|
+
resultKind: "paths",
|
|
178
|
+
pattern,
|
|
179
|
+
paths: paths.map((path) => formatToolDisplayPath(path, context.workspaceRoot)),
|
|
180
|
+
copyText: output
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const text = typeof result?.content === "string" ? result.content : output;
|
|
184
|
+
if (result && !Array.isArray(result.files) && typeof result.content !== "string") return null;
|
|
185
|
+
if (!result && jsonShape) return null;
|
|
186
|
+
const files = /* @__PURE__ */ new Map();
|
|
187
|
+
const append = (path, line, content) => {
|
|
188
|
+
const matches = files.get(path) ?? [];
|
|
189
|
+
matches.push({ line, text: content, key: `${line}:${matches.length}` });
|
|
190
|
+
files.set(path, matches);
|
|
191
|
+
};
|
|
192
|
+
if (Array.isArray(result?.files)) {
|
|
193
|
+
for (const value of result.files) {
|
|
194
|
+
const file = record(value);
|
|
195
|
+
if (typeof file?.path !== "string" || !file.path || !Array.isArray(file.matches)) return null;
|
|
196
|
+
for (const value2 of file.matches) {
|
|
197
|
+
const match = record(value2);
|
|
198
|
+
if (typeof match?.lineNumber !== "number" || !Number.isSafeInteger(match.lineNumber) || match.lineNumber < 1 || typeof match?.line !== "string")
|
|
199
|
+
return null;
|
|
200
|
+
append(file.path, match.lineNumber, match.line);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
for (const line of text.trimEnd().split("\n")) {
|
|
205
|
+
const match = /^(.+?):([1-9]\d*):(.*)$/u.exec(line);
|
|
206
|
+
if (!match || !Number.isSafeInteger(Number(match[2]))) return null;
|
|
207
|
+
append(match[1], Number(match[2]), match[3]);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
kind: "search",
|
|
212
|
+
resultKind: "matches",
|
|
213
|
+
pattern,
|
|
214
|
+
files: [...files].map(
|
|
215
|
+
([path, matches]) => ({
|
|
216
|
+
key: path,
|
|
217
|
+
path: formatToolDisplayPath(path, context.workspaceRoot),
|
|
218
|
+
matches
|
|
219
|
+
})
|
|
220
|
+
),
|
|
221
|
+
copyText: output
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function webUrl(value) {
|
|
225
|
+
if (typeof value !== "string") return null;
|
|
226
|
+
try {
|
|
227
|
+
const url = new URL(value);
|
|
228
|
+
return (url.protocol === "http:" || url.protocol === "https:") && !url.username && !url.password ? url.href : null;
|
|
229
|
+
} catch {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function webOverviewModel(context) {
|
|
234
|
+
if (!completed(context)) return null;
|
|
235
|
+
const tool = toolName(context);
|
|
236
|
+
if (context.entry.itemType !== "web_search" && tool !== "websearch" && tool !== "webfetch")
|
|
237
|
+
return null;
|
|
238
|
+
if (tool === "grep" || tool === "glob") return null;
|
|
239
|
+
const output = context.details.output?.text;
|
|
240
|
+
const parsed = parseJson(output);
|
|
241
|
+
if (!output) return null;
|
|
242
|
+
const result = record(parsed);
|
|
243
|
+
if (result?.isError === true || typeof result?.error === "string") return null;
|
|
244
|
+
const values = result?.sources ?? result?.results ?? (Array.isArray(parsed) ? parsed : void 0);
|
|
245
|
+
const input = record(parseJson(context.details.input?.text ?? context.entry.toolIo?.input));
|
|
246
|
+
const query = typeof input?.query === "string" ? input.query : context.entry.toolLabel?.detail ?? context.entry.label;
|
|
247
|
+
if (Array.isArray(values)) {
|
|
248
|
+
const sources = [];
|
|
249
|
+
for (const value of values) {
|
|
250
|
+
const source = record(value);
|
|
251
|
+
const url2 = webUrl(source?.url);
|
|
252
|
+
if (!url2 || source?.title !== void 0 && typeof source.title !== "string" || source?.snippet !== void 0 && typeof source.snippet !== "string")
|
|
253
|
+
return null;
|
|
254
|
+
sources.push({
|
|
255
|
+
key: `${url2}:${sources.length}`,
|
|
256
|
+
url: url2,
|
|
257
|
+
title: typeof source?.title === "string" ? source.title : url2,
|
|
258
|
+
...typeof source?.snippet === "string" ? { snippet: source.snippet } : {}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
if (result?.answer !== void 0 && typeof result.answer !== "string") return null;
|
|
262
|
+
return {
|
|
263
|
+
kind: "web",
|
|
264
|
+
query,
|
|
265
|
+
sources,
|
|
266
|
+
copyText: output,
|
|
267
|
+
...typeof result?.answer === "string" ? { answer: result.answer } : {}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const url = webUrl(result?.url);
|
|
271
|
+
const statusCode = result?.statusCode;
|
|
272
|
+
if (!url || typeof statusCode !== "number" || !Number.isInteger(statusCode) || statusCode < 100 || statusCode > 599)
|
|
273
|
+
return null;
|
|
274
|
+
const title = typeof result?.title === "string" ? result.title : url;
|
|
275
|
+
return {
|
|
276
|
+
kind: "web",
|
|
277
|
+
query: title,
|
|
278
|
+
sources: [{ key: url, url, title }],
|
|
279
|
+
copyText: output,
|
|
280
|
+
statusCode,
|
|
281
|
+
...typeof result?.text === "string" ? { answer: result.text } : {}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
var OVERVIEW_MODELS = [
|
|
285
|
+
commandOverviewModel,
|
|
286
|
+
readOverviewModel,
|
|
287
|
+
changeOverviewModel,
|
|
288
|
+
searchOverviewModel,
|
|
289
|
+
webOverviewModel
|
|
290
|
+
];
|
|
291
|
+
function resolveToolDisplayOverview(context) {
|
|
292
|
+
for (const model of OVERVIEW_MODELS) {
|
|
293
|
+
const presentation = model(context);
|
|
294
|
+
if (presentation) return presentation;
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
function recordedToolDisplayText(details) {
|
|
299
|
+
return [details.input, details.output, details.raw].filter((segment) => segment !== void 0).map((segment) => segment.text).join("\n\n");
|
|
300
|
+
}
|
|
301
|
+
export {
|
|
302
|
+
changeOverviewModel,
|
|
303
|
+
commandOverviewModel,
|
|
304
|
+
formatToolDisplayPath,
|
|
305
|
+
readOverviewModel,
|
|
306
|
+
recordedToolDisplayText,
|
|
307
|
+
resolveToolDisplayOverview,
|
|
308
|
+
searchOverviewModel,
|
|
309
|
+
webOverviewModel
|
|
310
|
+
};
|
package/locales/en.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"copy": "Copy available text",
|
|
3
|
+
"copied": "Copied",
|
|
4
|
+
"copyFailed": "Could not copy text",
|
|
5
|
+
"summary": "Summary",
|
|
6
|
+
"truncated": "Content was truncated",
|
|
7
|
+
"showMoreLines_one": "Show 1 more line",
|
|
8
|
+
"showMoreLines_other": "Show {{count}} more lines",
|
|
9
|
+
"showAllResults": "View all results",
|
|
10
|
+
"noSearchResults": "No results returned",
|
|
11
|
+
"noWebSources": "No sources returned",
|
|
12
|
+
"httpStatus": "HTTP {{status}}",
|
|
13
|
+
"hostUpgradeRequired": "This Ycodium app cannot render tool details from a plugin. Update the app."
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"copy": "复制可用文本",
|
|
3
|
+
"copied": "已复制",
|
|
4
|
+
"copyFailed": "无法复制文本",
|
|
5
|
+
"summary": "摘要",
|
|
6
|
+
"truncated": "已省略前面的内容",
|
|
7
|
+
"showMoreLines_one": "再显示 1 行",
|
|
8
|
+
"showMoreLines_other": "再显示 {{count}} 行",
|
|
9
|
+
"showAllResults": "查看所有结果",
|
|
10
|
+
"noSearchResults": "未返回结果",
|
|
11
|
+
"noWebSources": "未返回来源",
|
|
12
|
+
"httpStatus": "HTTP {{status}}",
|
|
13
|
+
"hostUpgradeRequired": "此 Ycodium 应用无法显示来自插件的工具详情。请更新应用。"
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ycodium-ai/plugin-tool-display",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Renders command, read, change, search, and web tool calls from recorded facts.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ycodium",
|
|
7
|
+
"ycodium-plugin"
|
|
8
|
+
],
|
|
9
|
+
"license": "UNLICENSED",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"assets",
|
|
13
|
+
"locales"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./dist/index.js",
|
|
18
|
+
"./models": {
|
|
19
|
+
"types": "./dist/models.d.ts",
|
|
20
|
+
"import": "./dist/models.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@tailwindcss/node": "^4.3.0",
|
|
28
|
+
"@tailwindcss/oxide": "^4.3.0",
|
|
29
|
+
"@types/react": "~19.2.14",
|
|
30
|
+
"@types/react-dom": "^19.2.3",
|
|
31
|
+
"esbuild": "0.28.1",
|
|
32
|
+
"lucide-react": "^0.564.0",
|
|
33
|
+
"react": "19.2.6",
|
|
34
|
+
"react-dom": "19.2.6",
|
|
35
|
+
"tailwindcss": "^4.0.0",
|
|
36
|
+
"typescript": "~6.0.3",
|
|
37
|
+
"vite-plus": "0.2.2",
|
|
38
|
+
"@ycodium-ai/plugin-api": "0.2.0"
|
|
39
|
+
},
|
|
40
|
+
"ycodium": {
|
|
41
|
+
"plugin": "./dist/index.js",
|
|
42
|
+
"displayName": "Tool display",
|
|
43
|
+
"description": "Renders command, read, change, search, and web tool calls from recorded facts.",
|
|
44
|
+
"icon": "blocks",
|
|
45
|
+
"ui": {
|
|
46
|
+
"assets": "./assets",
|
|
47
|
+
"entry": "ui.js"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "node build.mjs",
|
|
52
|
+
"typecheck": "tsgo --noEmit && tsgo --noEmit -p tsconfig.ui.json",
|
|
53
|
+
"test": "vp test run"
|
|
54
|
+
}
|
|
55
|
+
}
|