@kirkchinese/dsh-capability-catalog 0.1.3 → 0.1.4

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/client/client.js CHANGED
@@ -152,7 +152,7 @@ window.__ModuleLoader__.load({
152
152
  .cc-trigger:hover,.cc-trigger[aria-expanded=true]{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}
153
153
  .cc-trigger-badge{color:var(--dsw-alias-state-warn-primary)}
154
154
  .cc-anchor{position:relative;display:inline-flex}
155
- .cc-popover{position:absolute;bottom:calc(100% + 8px);left:0;z-index:50;width:380px;max-width:calc(100vw - 32px);max-height:min(560px,70vh);display:flex;flex-direction:column;gap:10px;padding:14px;border-radius:12px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-overlay);box-shadow:0 8px 28px rgba(0,0,0,.18);color:var(--dsw-alias-label-primary);font-size:13px}
155
+ .cc-popover{position:fixed;z-index:1000;box-sizing:border-box;width:380px;max-width:calc(100vw - 32px);display:flex;flex-direction:column;gap:10px;padding:14px;border-radius:12px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-overlay);box-shadow:0 8px 28px rgba(0,0,0,.18);color:var(--dsw-alias-label-primary);font-size:13px}
156
156
  .cc-popover-head{display:flex;align-items:center;justify-content:space-between;gap:8px;font-weight:600}
157
157
  .cc-popover-body{overflow:auto;display:flex;flex-direction:column;gap:12px;margin:0 -4px;padding:0 4px}
158
158
  .cc-sub{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary);margin-bottom:4px}
@@ -337,7 +337,41 @@ window.__ModuleLoader__.load({
337
337
  function SessionControl({ api, t, sessionId }) {
338
338
  const [open, setOpen] = useState(false)
339
339
  const [query, setQuery] = useState('')
340
+ const [placement, setPlacement] = useState(null)
340
341
  const anchor = useRef(null)
342
+ // Open toward the side with more room and stay inside the window. The popover is fixed to
343
+ // the viewport so scroll and clip containers of the composer cannot cut it off; the
344
+ // composer sits at the bottom in a session but in the middle of the window on a new one.
345
+ const place = useCallback(() => {
346
+ const node = anchor.current
347
+ if (!node) return
348
+ const rect = node.getBoundingClientRect()
349
+ const margin = 12
350
+ const gap = 8
351
+ const above = rect.top - margin - gap
352
+ const below = window.innerHeight - rect.bottom - margin - gap
353
+ const up = above >= 320 || above >= below
354
+ const room = Math.max(160, Math.min(560, up ? above : below))
355
+ const width = Math.min(380, window.innerWidth - 2 * margin)
356
+ const left = Math.max(margin, Math.min(rect.left, window.innerWidth - width - margin))
357
+ setPlacement({
358
+ position: 'fixed',
359
+ left: `${left}px`,
360
+ width: `${width}px`,
361
+ ...(up ? { bottom: `${window.innerHeight - rect.top + gap}px` } : { top: `${rect.bottom + gap}px` }),
362
+ maxHeight: `${room}px`,
363
+ })
364
+ }, [])
365
+ useEffect(() => {
366
+ if (!open) return undefined
367
+ place()
368
+ window.addEventListener('resize', place)
369
+ window.addEventListener('scroll', place, true)
370
+ return () => {
371
+ window.removeEventListener('resize', place)
372
+ window.removeEventListener('scroll', place, true)
373
+ }
374
+ }, [open, place])
341
375
  const { data, error, set, reload } = useOverview(api, sessionId, true)
342
376
  const [actionError, setActionError] = useState(null)
343
377
 
@@ -382,7 +416,7 @@ window.__ModuleLoader__.load({
382
416
  h('rect', { x: 2, y: 9, width: 5, height: 5, rx: 1 }), h('rect', { x: 9, y: 9, width: 5, height: 5, rx: 1 })),
383
417
  t.button,
384
418
  offCount > 0 ? h('span', { className: 'cc-trigger-badge' }, `−${offCount}`) : null),
385
- open ? h('div', { className: 'cc-popover', role: 'dialog', 'aria-label': t.sessionTitle },
419
+ open ? h('div', { className: 'cc-popover', role: 'dialog', 'aria-label': t.sessionTitle, style: placement || { visibility: 'hidden' } },
386
420
  h('div', { className: 'cc-popover-head' },
387
421
  h('span', null, t.sessionTitle),
388
422
  h('span', { className: 'cc-toolbar' },
@@ -23,7 +23,7 @@ export interface CompactOptions {
23
23
  * @param pointer - pointer such as `/properties/filters`; empty means the root.
24
24
  * @returns the subtree, or undefined when the pointer does not resolve.
25
25
  */
26
- export declare function resolvePointer(schema: unknown, pointer: string): unknown;
26
+ export declare function resolvePointer(schema: unknown, rawPointer: string): unknown;
27
27
  /**
28
28
  * Derive a compact, TypeScript-like signature from an object schema.
29
29
  * @param name - tool name used as the signature head.
@@ -5,7 +5,9 @@ const DEFAULT_OPTIONS = { maxDepth: 2, maxDescription: 160, maxEnum: 12 };
5
5
  * @param pointer - pointer such as `/properties/filters`; empty means the root.
6
6
  * @returns the subtree, or undefined when the pointer does not resolve.
7
7
  */
8
- export function resolvePointer(schema, pointer) {
8
+ export function resolvePointer(schema, rawPointer) {
9
+ // Accept a pointer copied with a trailing array marker, as earlier signatures rendered it.
10
+ const pointer = rawPointer.trim().replace(/(\[\])+$/u, '');
9
11
  if (pointer === '' || pointer === '/')
10
12
  return schema;
11
13
  if (!pointer.startsWith('/'))
@@ -96,7 +98,20 @@ function typeOf(schema, pointer, depth, options, lines, indent) {
96
98
  const type = Array.isArray(schema.type) ? schema.type.join(' | ') : schema.type;
97
99
  if (type === 'array') {
98
100
  const items = asSchema(schema.items);
99
- return items === undefined ? 'unknown[]' : `${wrap(typeOf(items, `${pointer}/items`, depth, options, lines, indent))}[]`;
101
+ if (items === undefined)
102
+ return 'unknown[]';
103
+ const inner = typeOf(items, `${pointer}/items`, depth, options, lines, indent);
104
+ // Keep exactly one pointer, last and clean: `{…3 fields}[] → /a/items`, never `→ /a/items[]`.
105
+ const arrows = inner.split(' → ').length - 1;
106
+ if (arrows === 0)
107
+ return `${wrap(inner)}[]`;
108
+ if (arrows === 1 && !inner.includes(' | ')) {
109
+ const arrow = inner.indexOf(' → ');
110
+ return `${inner.slice(0, arrow)}[]${inner.slice(arrow)}`;
111
+ }
112
+ // A union with collapsed branches: point at the item schema as a whole.
113
+ const branches = Array.isArray(items.anyOf) ? items.anyOf.length : Array.isArray(items.oneOf) ? items.oneOf.length : 0;
114
+ return `${branches > 0 ? `union<${branches}>` : 'item'}[] → ${pointer}/items`;
100
115
  }
101
116
  if (type === 'object' || schema.properties !== undefined) {
102
117
  const properties = asSchema(schema.properties);
@@ -132,7 +147,8 @@ function fields(schema, pointer, depth, options, lines, indent) {
132
147
  lines.push(`${indent}}`);
133
148
  continue;
134
149
  }
135
- type = `${type}${constraints(node)}`;
150
+ const arrow = type.lastIndexOf(' → ');
151
+ type = arrow >= 0 ? `${type.slice(0, arrow)}${constraints(node)}${type.slice(arrow)}` : `${type}${constraints(node)}`;
136
152
  lines.push(`${indent}${key}${optional}: ${type}${defaultText}${description}`);
137
153
  lines.push(...nested);
138
154
  }
@@ -262,7 +262,7 @@ export class McpSource {
262
262
  throw new Error(`${this.key}: streamable-http transport requires "url"`);
263
263
  transport = new StreamableHTTPClientTransport(new URL(config.url), { requestInit: { headers: { ...config.headers } } });
264
264
  }
265
- const client = new Client({ name: 'dsh-capability-catalog', version: '0.1.3' });
265
+ const client = new Client({ name: 'dsh-capability-catalog', version: '0.1.4' });
266
266
  client.onclose = () => {
267
267
  if (this.client === client)
268
268
  this.client = undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kirkchinese/dsh-capability-catalog",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Lazy capability catalog for DeepSeek Harness: one compact index plus search/describe/call meta-tools for MCP tools and skills, instead of every schema on every request",
5
5
  "keywords": [
6
6
  "deepseek-harness",