@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 +17 -0
- package/package.json +1 -1
- package/src/index.d.ts +2 -2
- package/src/runtime.mjs +24 -18
- package/src/view.mjs +1 -1
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
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
|
|
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',
|
|
18
|
-
ensure(Buffer.byteLength(value) <=
|
|
19
|
-
'payload_too_large',
|
|
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',
|
|
23
|
-
ensure(value.length > 0, 'invalid_payload',
|
|
24
|
-
ensure(value.length <= 32, 'payload_too_large',
|
|
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',
|
|
27
|
+
ensure(object(span), 'invalid_payload', `Invalid ${label} span`)
|
|
28
28
|
ensure(typeof span.text === 'string' && !HOVER_FORBIDDEN.test(span.text),
|
|
29
|
-
'invalid_payload',
|
|
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
|
|
33
|
+
!HOVER_FORBIDDEN.test(span[field]), 'invalid_payload', `Invalid ${label} span ${field}`)
|
|
34
34
|
ensure(Buffer.byteLength(span[field]) <= 64,
|
|
35
|
-
'payload_too_large',
|
|
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
|
|
40
|
+
'invalid_payload', `Invalid ${label} span ${field}`)
|
|
41
41
|
}
|
|
42
|
-
ensure(span.actionId === undefined, 'invalid_payload',
|
|
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',
|
|
47
|
-
ensure(Buffer.byteLength(text) <=
|
|
48
|
-
'payload_too_large',
|
|
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')
|
|
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)
|
|
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 }),
|