@standardagents/code-plugin-sdk 1.0.0-alpha.7-theme.0 → 1.0.0-alpha.8-title.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 CHANGED
@@ -198,3 +198,20 @@ can install a published prerelease by its exact version.
198
198
  ## License
199
199
 
200
200
  MIT. See `LICENSE`.
201
+
202
+ Sidebar card titles use muted, regular-weight text. The host prefixes a plugin
203
+ identity icon only on the viewer's Nerd Font symbol tier. Other tiers render
204
+ the title alone. This applies to bordered and borderless cards. Empty titles
205
+ remain empty; no author-supplied icon or font detection is required.
206
+
207
+ A card view may replace its declared title through
208
+ `ui.view(root, { title: [{ text: 'Builds ' }, { text: '●', foreground: 'green' }] })`.
209
+ The optional `title` uses native `TextSpan` fields, including `#RRGGBB` and the terminal colors `black`, `red`, `green`,
210
+ `yellow`, `blue`, `magenta`, `cyan`, and `white`;
211
+ `ui.span()` is a body-view builder with a different shape. Titles accept 1–32
212
+ passive spans and at most 512 UTF-8 bytes of combined, nonblank single-line text.
213
+ Actions, control characters, and bidi formatting are rejected. Each replacement
214
+ updates the body and title together. Omitting `title` restores the manifest title.
215
+ This option belongs to card views. Plugins own the meaning and color of any
216
+ status light; the SDK adds no health indicator. Host title styling and supported
217
+ Nerd Font identity icons apply around the author-supplied spans.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standardagents/code-plugin-sdk",
3
- "version": "1.0.0-alpha.7-theme.0",
3
+ "version": "1.0.0-alpha.8-title.0",
4
4
  "type": "module",
5
5
  "description": "Standard Code plugin authoring SDK",
6
6
  "license": "MIT",
package/src/index.d.ts CHANGED
@@ -157,7 +157,7 @@ export interface ButtonNode { type: 'button'; label: string; action: ViewAction
157
157
  export type ViewNode = StackNode | RowNode | DividerNode | TextNode | BadgeNode | DotNode | ProgressNode |
158
158
  SegmentsNode | CardNode | StatNode | KvNode | TabsNode | SelectNode | TableNode | LogNode | ButtonNode;
159
159
  /** A host-rendered view tree. Cards, panels, and sections accept it. */
160
- export interface ViewContent { kind: 'view'; root: ViewNode }
160
+ export interface ViewContent { kind: 'view'; root: ViewNode; /** Passive styled title for card views. */ title?: TextSpan[] }
161
161
 
162
162
  /** Slots and overlays accept rows, text, or canvas. */
163
163
  export type DrawnContent = RowsContent | TextContent | CanvasContent;
@@ -410,7 +410,7 @@ export function validateView(root: unknown, options?: { kind?: SurfaceKind; mani
410
410
  type Options<T> = Omit<T, 'type' | 'children'>;
411
411
  /** Optional builders. Each returns the plain protocol JSON for one node; hand-written JSON is equivalent. */
412
412
  export const ui: {
413
- view(root: ViewNode): ViewContent;
413
+ view(root: ViewNode, options?: { title?: TextSpan[] }): ViewContent;
414
414
  action(actionId: string, options?: { value?: string; opens?: string }): ViewAction;
415
415
  span(text: string, options?: Omit<ViewSpan, 'text'>): ViewSpan;
416
416
  stack(children: ViewNode[], options?: Options<StackNode>): StackNode;
package/src/runtime.mjs CHANGED
@@ -11,41 +11,41 @@ function boundedText(value, label, limit = 512) {
11
11
  return value
12
12
  }
13
13
  const HOVER_FORBIDDEN = /[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/
14
- function validateCanvasHover(value) {
14
+ function validatePassiveText(value, limit = LIMITS.canvasHoverBytes, label = 'canvas hover') {
15
15
  if (typeof value === 'string') {
16
16
  ensure(value.trim().length > 0 && !HOVER_FORBIDDEN.test(value),
17
- 'invalid_payload', 'Invalid canvas hover text')
18
- ensure(Buffer.byteLength(value) <= LIMITS.canvasHoverBytes,
19
- 'payload_too_large', `Canvas hover text exceeds ${LIMITS.canvasHoverBytes} bytes`)
17
+ 'invalid_payload', `Invalid ${label} text`)
18
+ ensure(Buffer.byteLength(value) <= limit,
19
+ 'payload_too_large', `${label} text exceeds ${limit} bytes`)
20
20
  return
21
21
  }
22
- ensure(Array.isArray(value), 'invalid_payload', 'Invalid canvas hover spans')
23
- ensure(value.length > 0, 'invalid_payload', 'Canvas hover spans must contain text')
24
- ensure(value.length <= 32, 'payload_too_large', 'Canvas hover spans exceed 32 spans')
22
+ ensure(Array.isArray(value), 'invalid_payload', `Invalid ${label} spans`)
23
+ ensure(value.length > 0, 'invalid_payload', `${label} spans must contain text`)
24
+ ensure(value.length <= 32, 'payload_too_large', `${label} spans exceed 32 spans`)
25
25
  let text = ''
26
26
  for (const span of value) {
27
- ensure(object(span), 'invalid_payload', 'Invalid canvas hover span')
27
+ ensure(object(span), 'invalid_payload', `Invalid ${label} span`)
28
28
  ensure(typeof span.text === 'string' && !HOVER_FORBIDDEN.test(span.text),
29
- 'invalid_payload', 'Invalid canvas hover span text')
29
+ 'invalid_payload', `Invalid ${label} span text`)
30
30
  for (const field of ['foreground', 'background']) {
31
31
  if (span[field] !== undefined) {
32
32
  ensure(typeof span[field] === 'string' && span[field].trim().length > 0 &&
33
- !HOVER_FORBIDDEN.test(span[field]), 'invalid_payload', `Invalid canvas hover span ${field}`)
33
+ !HOVER_FORBIDDEN.test(span[field]), 'invalid_payload', `Invalid ${label} span ${field}`)
34
34
  ensure(Buffer.byteLength(span[field]) <= 64,
35
- 'payload_too_large', `Canvas hover span ${field} exceeds 64 bytes`)
35
+ 'payload_too_large', `${label} span ${field} exceeds 64 bytes`)
36
36
  }
37
37
  }
38
38
  for (const field of ['bold', 'italic', 'underline']) {
39
39
  ensure(span[field] === undefined || typeof span[field] === 'boolean',
40
- 'invalid_payload', `Invalid canvas hover span ${field}`)
40
+ 'invalid_payload', `Invalid ${label} span ${field}`)
41
41
  }
42
- ensure(span.actionId === undefined, 'invalid_payload', 'Canvas hover spans cannot contain actionId')
42
+ ensure(span.actionId === undefined, 'invalid_payload', `${label} spans cannot contain actionId`)
43
43
  text += span.text
44
44
  }
45
45
  ensure(text.trim().length > 0 && !HOVER_FORBIDDEN.test(text),
46
- 'invalid_payload', 'Invalid canvas hover text')
47
- ensure(Buffer.byteLength(text) <= LIMITS.canvasHoverBytes,
48
- 'payload_too_large', `Canvas hover text exceeds ${LIMITS.canvasHoverBytes} bytes`)
46
+ 'invalid_payload', `Invalid ${label} text`)
47
+ ensure(Buffer.byteLength(text) <= limit,
48
+ 'payload_too_large', `${label} text exceeds ${limit} bytes`)
49
49
  }
50
50
  function validateEntity(entity) {
51
51
  ensure(object(entity) && entityKinds.includes(entity.kind), 'invalid_payload', 'Invalid registration entity')
@@ -91,7 +91,13 @@ function validateContent(content, declaration, manifest) {
91
91
  ensure(object(content) && CONTENT_KINDS.includes(content.kind), 'invalid_payload', 'Invalid surface content')
92
92
  ensure(acceptsContent(kind, content), 'invalid_payload', `A ${kind} contribution cannot show ${content.kind} content`)
93
93
  // The view walk bounds depth before serialization visits the tree.
94
- if (content.kind === 'view') validateView(content.root, { kind: declaration.kind, manifest })
94
+ if (content.kind === 'view') {
95
+ validateView(content.root, { kind: declaration.kind, manifest })
96
+ if (content.title !== undefined) {
97
+ ensure(kind === 'card' && Array.isArray(content.title), 'invalid_payload', 'Styled titles belong to card views')
98
+ validatePassiveText(content.title, 512, 'card title')
99
+ }
100
+ }
95
101
  jsonBytes(content)
96
102
  if (content.kind === 'canvas') {
97
103
  const { columns, rows, shade = 0, hover, themeColors } = content.canvas ?? {}
@@ -108,7 +114,7 @@ function validateContent(content, declaration, manifest) {
108
114
  'invalid_payload', 'Invalid canvas theme color recipe')
109
115
  }
110
116
  }
111
- if (hover !== undefined) validateCanvasHover(hover)
117
+ if (hover !== undefined) validatePassiveText(hover)
112
118
  }
113
119
  }
114
120
 
package/src/view.mjs CHANGED
@@ -231,7 +231,7 @@ const node = (type, fields) => compact({ type, ...fields })
231
231
 
232
232
  /** Optional builders. Each returns the plain protocol JSON for one node. */
233
233
  export const ui = Object.freeze({
234
- view: root => ({ kind: 'view', root }),
234
+ view: (root, { title } = {}) => compact({ kind: 'view', root, title }),
235
235
  action: (actionId, { value, opens } = {}) => compact({ actionId, value, opens }),
236
236
  span: (value, { tone, weight, mono } = {}) => compact({ text: value, tone, weight, mono }),
237
237
  stack: (items, { gap } = {}) => node('stack', { gap, children: items }),