@standardagents/code-plugin-sdk 1.0.0-alpha.4-hover.1 → 1.0.0-alpha.4-hover.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
@@ -11,8 +11,8 @@ Its `package.json` includes a static `standardPlugin` manifest.
11
11
  {
12
12
  "name": "example-status",
13
13
  "type": "module",
14
- "peerDependencies": { "@standardagents/code-plugin-sdk": "^1.0.0-alpha.4-hover.1" },
15
- "devDependencies": { "@standardagents/code-plugin-sdk": "^1.0.0-alpha.4-hover.1" },
14
+ "peerDependencies": { "@standardagents/code-plugin-sdk": "^1.0.0-alpha.4-hover.2" },
15
+ "devDependencies": { "@standardagents/code-plugin-sdk": "^1.0.0-alpha.4-hover.2" },
16
16
  "standardPlugin": {
17
17
  "apiVersion": 1,
18
18
  "id": "example-status",
@@ -46,9 +46,9 @@ plugin's own test suite.
46
46
 
47
47
  The complete rendering contract is in [REFERENCE.md](./REFERENCE.md). It
48
48
  covers rows, styled text, badges, cards, slots, panels, overlays, host-rendered
49
- views, ANSI canvases, canvas hover text, input focus, popover/column/pane
50
- presentations, remote read-only behavior, and the frontend surfaces that are
51
- still planned.
49
+ views, ANSI canvases, styled canvas hover text, input focus,
50
+ popover/column/pane presentations, remote read-only behavior, and the frontend
51
+ surfaces that are still planned.
52
52
 
53
53
  Plugin source imports the default package entry. Plugin tests may import
54
54
  `@standardagents/code-plugin-sdk/testing` for `createHarness`. Runner internals,
package/REFERENCE.md CHANGED
@@ -5,7 +5,7 @@ The public declarations live in
5
5
  plugin authors.
6
6
 
7
7
  The bundled SDK and the npm package use the same public declarations. This
8
- reference describes SDK `1.0.0-alpha.4-hover.1`. Plugins should declare the
8
+ reference describes SDK `1.0.0-alpha.4-hover.2`. Plugins should declare the
9
9
  version published for their product build in both `peerDependencies` and
10
10
  `devDependencies`; the manifest `apiVersion` remains `1`.
11
11
 
@@ -139,11 +139,15 @@ wraps or scrolls. Wide characters take two cells. Cells in default colours
139
139
  take the viewer's theme, and a blank default cell shows the surface under the
140
140
  canvas.
141
141
 
142
- `hover` is an optional one-line string. It must be non-empty after trimming,
143
- fit within 512 UTF-8 bytes, and contain no C0, C1, DEL, or bidirectional
144
- formatting character. `Canvas.replace` publishes the canvas metadata, so a
145
- plugin can update the hover text with another `replace` call while retaining
146
- the same canvas grid.
142
+ `hover` is an optional one-line string or an array of styled `TextSpan` values.
143
+ The string must be non-empty after trimming, fit within 512 UTF-8 bytes, and
144
+ contain no C0, C1, DEL, or bidirectional formatting character. Styled hover
145
+ values contain at most 32 spans whose joined text follows the same bounds and
146
+ must be non-empty. Span styling supports `foreground`, `background`, `bold`,
147
+ `italic`, and `underline`; `actionId` is rejected because hover text is
148
+ passive. `Canvas.replace` publishes the canvas metadata, so a plugin can update
149
+ the hover text with another `replace` call while retaining the same canvas
150
+ grid.
147
151
 
148
152
  On the current frontend, live canvases in `machine.before` and
149
153
  `machine.after` slots are hover targets. The viewer waits 300 milliseconds
@@ -152,6 +156,8 @@ line owned by the host. Leaving the canvas dismisses it immediately. Hover
152
156
  does not focus the canvas or change its geometry. The dwell timer and pointer
153
157
  state belong to each viewer, so remote viewers can see hover text for a
154
158
  machine slot independently.
159
+ Hover overlays for these machine slots use the same 22% sidebar machine colour
160
+ blend as the machine name bar over the viewer's background.
155
161
 
156
162
  `focus(true)` asks for keyboard input and needs `captureInput: true` in the
157
163
  canvas spec. While the canvas is shown in a pop-over, column, or plugin pane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@standardagents/code-plugin-sdk",
3
- "version": "1.0.0-alpha.4-hover.1",
3
+ "version": "1.0.0-alpha.4-hover.2",
4
4
  "type": "module",
5
5
  "description": "Standard Code plugin authoring SDK",
6
6
  "license": "MIT",
package/src/index.d.ts CHANGED
@@ -90,8 +90,8 @@ export interface CanvasSpec {
90
90
  transparent?: boolean;
91
91
  shade?: number;
92
92
  captureInput?: boolean;
93
- /** A bounded one-line string shown while the pointer rests over the canvas. */
94
- hover?: string;
93
+ /** A bounded one-line string or styled spans shown while the pointer rests over the canvas. */
94
+ hover?: string | TextSpan[];
95
95
  }
96
96
  export type CanvasContent = { kind: 'canvas'; canvas: CanvasSpec };
97
97
  /**
package/src/runtime.mjs CHANGED
@@ -12,9 +12,39 @@ function boundedText(value, label, limit = 512) {
12
12
  }
13
13
  const HOVER_FORBIDDEN = /[\u0000-\u001f\u007f-\u009f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/
14
14
  function validateCanvasHover(value) {
15
- ensure(typeof value === 'string' && value.trim().length > 0 && !HOVER_FORBIDDEN.test(value),
15
+ if (typeof value === 'string') {
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`)
20
+ return
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')
25
+ let text = ''
26
+ for (const span of value) {
27
+ ensure(object(span), 'invalid_payload', 'Invalid canvas hover span')
28
+ ensure(typeof span.text === 'string' && !HOVER_FORBIDDEN.test(span.text),
29
+ 'invalid_payload', 'Invalid canvas hover span text')
30
+ for (const field of ['foreground', 'background']) {
31
+ if (span[field] !== undefined) {
32
+ ensure(typeof span[field] === 'string' && span[field].trim().length > 0 &&
33
+ !HOVER_FORBIDDEN.test(span[field]), 'invalid_payload', `Invalid canvas hover span ${field}`)
34
+ ensure(Buffer.byteLength(span[field]) <= 64,
35
+ 'payload_too_large', `Canvas hover span ${field} exceeds 64 bytes`)
36
+ }
37
+ }
38
+ for (const field of ['bold', 'italic', 'underline']) {
39
+ ensure(span[field] === undefined || typeof span[field] === 'boolean',
40
+ 'invalid_payload', `Invalid canvas hover span ${field}`)
41
+ }
42
+ ensure(span.actionId === undefined, 'invalid_payload', 'Canvas hover spans cannot contain actionId')
43
+ text += span.text
44
+ }
45
+ ensure(text.trim().length > 0 && !HOVER_FORBIDDEN.test(text),
16
46
  'invalid_payload', 'Invalid canvas hover text')
17
- ensure(Buffer.byteLength(value) <= LIMITS.canvasHoverBytes,
47
+ ensure(Buffer.byteLength(text) <= LIMITS.canvasHoverBytes,
18
48
  'payload_too_large', `Canvas hover text exceeds ${LIMITS.canvasHoverBytes} bytes`)
19
49
  }
20
50
  function validateEntity(entity) {