@ai-ecoverse/slicc-spectrum 2.10.0 → 2.11.1

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
@@ -257,7 +257,7 @@ The chevron only expands and collapses; a click on the row, <kbd>Enter</kbd> or
257
257
  <kbd>→</kbd> expands a cone and then moves to its first scoop, <kbd>←</kbd> moves from a scoop to its cone and then collapses it, and neither changes the active agent.
258
258
  Several cones can be open at once, and they stay open for every view of the panel on the same model, so a layout switch keeps them; when the active agent has changed since the panel last showed it, its cone opens again as the panel mounts, so on a phone a cone you tapped still shows its scoops when you come back to Agents, and a cone you closed stays closed.
259
259
  When the active scoop's cone is collapsed, the cone row takes the Tab stop.
260
- A row's first line starts with a dot coloured by status, always in the same place, and, when there are unread messages, the unread count beside it (`99+` from a hundred up, at `--swc-font-size-50`, white on the accent fill, or on the negative fill when the cone is in error).
260
+ A row's first line starts with a status mark shaped by status (a filled disk when idle, a pulsing ringed disk when working that `prefers-reduced-motion` stills, a dashed ring when thinking, a hollow ring when waiting, and an alert triangle when in error), always in the same place, and, when there are unread messages, the unread count beside it (`99+` from a hundred up, at `--swc-font-size-50`, white on the accent fill, or on the negative fill when the cone is in error).
261
261
  Then comes the name; the context fill, when it's high, sits in the top right corner.
262
262
  Every row has up to two lines of conversation title, aligned to the top under the name, with no status word, so nothing moves when the status changes; the accessible name and the tooltip say the status and the exact unread count.
263
263
  A cone with scoops has a ribbon line below: one segment per scoop, coloured by status at 3:1 or more on every row background, then its scoops in words, naming only the busy ones, as in "2 scoops · 1 waiting" ("2 scoops" when all are idle).
@@ -266,7 +266,7 @@ Rows without scoops or errors have no empty ribbon or error line.
266
266
  With no agents while the agent port's `starting()` returns a line, the panel says "The agent shows up here once it starts." (`[data-empty="starting"]`, subdued) and has no **New cone**; the line goes, and New cone comes back, once an agent arrives or the line clears.
267
267
  The drop or delete button sits in its own column on the row's right edge, under the context fill, so the title ellipsizes before it and the focus ring stays whole; it shows on hover, on keyboard focus of the row, when <kbd>Tab</kbd> from the selected row reaches it, and always on touch, and it hides again once a pointer that clicked it leaves.
268
268
  The selected row has an accent bar on its inline-start edge.
269
- Under forced colors, the dots, segments, unread circle and selection bar keep `CanvasText`, `Highlight` and `HighlightText`.
269
+ Under forced colors, the status marks keep their shapes in `CanvasText`, and the segments, unread circle and selection bar keep `CanvasText`, `Highlight` and `HighlightText`.
270
270
  A cone with scoops carries `aria-expanded`, and `aria-controls` names its scoop rows while they show.
271
271
  Expanding and collapsing have no motion.
272
272
  **New cone** sits at the bottom.
@@ -301,7 +301,7 @@ While pending, cards leave the log for their own region between the header and t
301
301
  Clicking a waiting card (not its controls), or <kbd>Enter</kbd> on it, scrolls the log to where it was asked, at the bottom of the view above the composer.
302
302
  When a card leaves the region (answered, timed out or decided elsewhere) while it has focus, focus moves to the newest card still waiting, else the composer; a timed-out card goes back to its place in the log.
303
303
  Under `prefers-reduced-motion`, nothing animates and the scroll is instant.
304
- The header is one row: a dot coloured by status (`data-agent-status`, with the status in its `aria-label` and tooltip, and no word, so the pickers never move; in forced colours it paints `CanvasText`, like the Agents markers), a model picker that names each model's provider and leaves out classifiers and models with `tools: false` (with no model on offer, **Set up a model**, `data-action="set-up-model"`, opens Settings), the thinking picker, the context meter and **New conversation**, which calls `clear` and puts focus back in the composer; on a cone, when the port can freeze, its tooltip says the old conversation stays in the Freezer.
304
+ The header is one row: a status mark shaped by status (`data-agent-status`, with the status in its `aria-label` and tooltip, focusable so the keyboard reaches it, and no visible word, so the pickers never move; in forced colours its shape paints `CanvasText`, like the Agents markers), a model picker that names each model's provider and leaves out classifiers and models with `tools: false` (with no model on offer, **Set up a model**, `data-action="set-up-model"`, opens Settings), the thinking picker, the context meter and **New conversation**, which calls `clear` and puts focus back in the composer; on a cone, when the port can freeze, its tooltip says the old conversation stays in the Freezer.
305
305
  It never wraps: when it's short of room, the model picker's label is cut with an ellipsis, and under 560px wide (`header[data-compact]`) New conversation is icon-only, with `accessible-label` and its tooltip.
306
306
  The pickers re-read `models()` on `settings`, `accounts` and `agents`.
307
307
  When a live agent's model, or `Settings.model`, is no longer offered and its provider (the id before `/`) isn't a connected account, it moves to the offered chat model with `default: true`, else the first one; frozen agents are left alone, and so is everything on a follower unless it has Control and is connected.
@@ -1002,8 +1002,8 @@ An approval whose `kind` this build doesn't know shows neither a dialog nor a no
1002
1002
  #### `live` (optional)
1003
1003
 
1004
1004
  Later backed by: BIOS.
1005
- `items()` returns the `LiveItem`s, what is live in seven now (a recording, later an open serial port): `id`, `kind` (free text: `screen`, `camera`, `microphone`, later `serial`), `label` (such as "Recording screen"), `what` (may be empty), `asker`, `since` (ms) and `action` (`{ label }`, such as "Stop"); emit `items` when the list changes. `act(id)` runs the item's action.
1006
- `<slicc-app>` shows `<slicc-live>` under the header when the port is there: nothing while `items()` is empty, else one chip per item, stacked (`[data-surface=live]`, each chip `[data-id]`): a red dot, then "{label} · {what} · {asker} · m:ss" (`what` left out when empty), ticking once a second from `since`, and a quiet button with `action.label` (`data-action="live-act"`) that calls `act(id)`. On phones (`compact`) a chip keeps only the dot, the label and the button.
1005
+ `items()` returns the `LiveItem`s, what is live in seven now (a recording, later an open serial port): `id`, `kind` (free text: `screen`, `camera`, `microphone`, later `serial`), `label` (such as "Recording screen"), `what` (may be empty), `asker`, `since` (ms), optional `endedAt` (ms, when the item has finished; leave it out for a live chip) and `action` (`{ label }`, such as "Stop" or "Dismiss"); emit `items` when the list changes. `act(id)` runs the item's action.
1006
+ `<slicc-app>` shows `<slicc-live>` under the header when the port is there: nothing while `items()` is empty, else one chip per item, stacked (`[data-surface=live]`, each chip `[data-id]`): a red dot, then "{label} · {what} · {asker} · m:ss" (`what` left out when empty), ticking once a second from `since`, and a quiet button with `action.label` (`data-action="live-act"`) that calls `act(id)`. With `endedAt` the chip carries `data-ended`, uses a neutral (not red) dot with no pulse, replaces the ticking timer with "Ended" (frozen from `endedAt`, so the elapsed time stays put without a timer), and keeps the same act button (bios sets the label to **Dismiss** and removes the item). The strip only ticks while at least one item has no `endedAt`. On phones (`compact`) a live chip keeps only the dot, the label and the button; an ended chip also keeps "Ended" next to the label. Spectrum does not rewrite `label`: bios owns that wording and should use past tense for ended items (for example "Recorded microphone"). The dummy's `live: 'ended'` (or `?live=ended` on `/ui/`) stacks one live screen chip next to an ended microphone chip.
1007
1007
 
1008
1008
  #### `capture` (optional)
1009
1009
 
@@ -1144,7 +1144,7 @@ Like slicc-agent's snapshot, the dummy's vitals are the budget, Spend, Active ag
1144
1144
  `controls-cr` and `controls-lf` show control characters in a sudo command's quoted word; `controls-place` shows them in the requester name, place and target; `controls-cwd` shows them in the cwd.
1145
1145
  `createDummyModel({ asks: 'long' })` (`?asks=long`) adds a pending sudo request with that seven-line command to kitchen-sink (`k-long`).
1146
1146
  `createDummyModel({ approvals: 'click' | 'click-remote' | 'capture' })` adds one capture request: a click request from sliccy for `media` to choose what to share (`click-share`, `here: true`), the same with `here: false` on Studio Mac (`click-remote`), or a guest capture from Seat 2 (Mara) through sliccy, recording the screen for up to 10 seconds with the microphone, with a level 2 verdict (`capture-screen`).
1147
- `createDummyModel({ live: 'screen' | 'both' })` (`?live=` on `/ui/`) adds a `live` port with sliccy recording the entire screen (`live-screen`), and with `both` also Seat 2 (Mara) via docs-pass recording the desk microphone (`live-microphone`); `act(id)` records the id in `model.live.acted` and drops the item, and `model.live.add(item)` adds one.
1147
+ `createDummyModel({ live: 'screen' | 'both' | 'ended' })` (`?live=` on `/ui/`) adds a `live` port with sliccy recording the entire screen (`live-screen`), and with `both` also Seat 2 (Mara) via docs-pass recording the desk microphone (`live-microphone`); `ended` keeps the live screen chip and ends the microphone chip (`endedAt`, **Dismiss**); `act(id)` records the id in `model.live.acted` and drops the item, and `model.live.add(item)` adds one.
1148
1148
  `createDummyModel({ switches: 'capture' })` (`?switches=capture`) adds a `switches` port with Camera, Microphone and Screen, all on; `set` records `[key, on]` in `model.switches.sets`.
1149
1149
  Kitchen-sink's "Here is where things stand." ends with a `media` call that waited for a click in seven's tab (`k-media`).
1150
1150
  Decisions are recorded in `model.approvals.decisions`, and `model.approvals.end(id, reason)` ends a request.
@@ -4356,18 +4356,26 @@ function liveFixtures(scenario, now2 = Date.now()) {
4356
4356
  action: { label: "Stop" }
4357
4357
  };
4358
4358
  if (scenario === "screen") return [screen];
4359
- return [
4360
- screen,
4361
- {
4362
- id: "live-microphone",
4363
- kind: "microphone",
4364
- label: "Recording microphone",
4365
- what: "Desk microphone",
4366
- asker: "Seat 2 (Mara) via docs-pass",
4367
- since: now2 - 7e3,
4368
- action: { label: "Stop" }
4369
- }
4370
- ];
4359
+ const microphone = {
4360
+ id: "live-microphone",
4361
+ kind: "microphone",
4362
+ label: "Recording microphone",
4363
+ what: "Desk microphone",
4364
+ asker: "Seat 2 (Mara) via docs-pass",
4365
+ since: now2 - 7e3,
4366
+ action: { label: "Stop" }
4367
+ };
4368
+ if (scenario === "ended") {
4369
+ return [
4370
+ screen,
4371
+ {
4372
+ ...microphone,
4373
+ endedAt: now2 - 1e3,
4374
+ action: { label: "Dismiss" }
4375
+ }
4376
+ ];
4377
+ }
4378
+ return [screen, microphone];
4371
4379
  }
4372
4380
  var DummyLive = class extends Emitter {
4373
4381
  #items;