@ai-ecoverse/slicc-spectrum 1.75.0 → 1.76.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
@@ -137,7 +137,7 @@ Text is Adobe Clean Spectrum VF, falling back to Adobe Clean, and code is Spectr
137
137
  | `<slicc-app>` | The shell: header with the agent picker, status items, the update status, the network indicator, the tray, the View menu and the theme switch, rails, and a `<slicc-dock>`. Saves the dock layout per screen class to `storage` (default `localStorage`) under `layoutKey`. While connected it sets `overscroll-behavior` to `none !important` on the page's `html` and `body`, so only scroll areas inside panels rubber-band. It sets the page's `color-scheme` to its resolved theme, and when the page has a `theme-color` meta per `prefers-color-scheme`, gives every one of them the content of the meta for that theme, so the page and the installed app's title bar follow the app's setting instead of the OS. When the last app on the page is removed, the page's own inline values and `theme-color` contents come back. With a `follow` port, the header shows a `<slicc-follow>` chip, and while the follower connects, waits to be allowed, is unlinked or fails, a `<slicc-follow page>` (`.alone[data-follow]` with the page kind) takes the place of the panels; without Terminal access, the terminal surface and New terminal aren't offered, and open terminals close when it's taken away. |
138
138
  | `<slicc-dock>` | Generic. [dockview-core](https://dockview.dev/) 8 with a theme mapped to Spectrum tokens, in its own shadow root. Panels come from `factories` (`name → (params) => HTMLElement`). `open`, `close`, `has`, `content`, `focusPanel`, `cycleGroup`, `cycleTab`, `toJSON`, `restore`, `clear`, and the full `api`. Tabs have a context menu (close, close others, float, maximize). Events: `layout-change`, `active-panel-change`, `panel-close`. |
139
139
  | `<slicc-agents>` | A tree (`role="tree"`) of cones, with their scoops hidden under them. A row's first line starts with one circle: its unread count when there are unread messages (`9+` from ten up, white on the accent fill), otherwise a dot coloured by status. Then comes the name; the context fill, when it's high, sits in the top right corner. Every row has up to two lines starting with its status in words (`.state`, such as "Working"), then its conversation title, so the status stays visible when the unread count takes the circle; the accessible name says the status and the exact unread count too. A cone's row also has a ribbon along its bottom with one fixed-width segment per scoop, coloured by status; it's `aria-hidden`, and the cone's `aria-description` sums it up, as in "2 scoops: 1 working, 1 idle". On hover, on keyboard focus of the row, or when <kbd>Tab</kbd> from the selected row reaches its drop or delete button, a strip the full height of the row's right edge holds that button over the text; the context fill stays on top of it, and it hides again once a pointer that clicked it leaves. A click, <kbd>Enter</kbd> or <kbd>Space</kbd> on a cone expands or collapses it, as do <kbd>→</kbd> and <kbd>←</kbd> (<kbd>→</kbd> on an expanded cone moves to its first scoop, <kbd>←</kbd> on a scoop to its cone); only the focused cone is expanded, and its scoops list below it. A cone with scoops carries `aria-expanded`, and `aria-controls` names its scoop rows while they show. Expanding and collapsing have no motion. **New cone** sits at the bottom. The conversation title is also shown in the header's cone picker: `Agent.title`, or else the first line of the first user message, or "No messages yet". When cones share a name, their accessible names and the Delete cone, Freezer and confirmation labels add the title, as in "Delete cone sliccy (Hello from Bedrock)". Selecting one makes it the active agent. |
140
- | `<slicc-chat>` | One thread's conversation (its `agent`, or the active agent if unset), by day. A message's time shows only when it is at least two minutes newer than the last time shown, and the first one after each day separator always shows; a hidden time stays for screen readers (`time.sr` with `datetime`, or `.sr` on licks and cards), and a meta row left empty isn't rendered. User messages (with attachments and a quiet timestamp, `time.when`, by that rule; the reader's own carry no name or delivery tag, while `data-mode` and `data-delivered` still say how they went; a message from another cone, a follower or a guest says who sent it, and an agent's request carries its name and an Agent badge; a message from another agent, an agent's request or a message `from` a cone (`origin` `agent`, or `from` set without `origin` `follower` or `guest`), is `[data-remote]`: a subtle indigo bubble and a 🤖 pill on its bottom edge, `aria-hidden` since the byline names the sender; people, the reader included, never get it), assistant messages (with the time by that rule, and the model only on the first reply and whenever it changes from the previous one, `.meta .model` with the usage as its tooltip; without a model the usage is the time's tooltip, and with neither it is the reply's own (`.reply[title]`); the turn always carries the usage as its `aria-description`; the cone's name isn't repeated; Markdown with tables, task lists, quotes, code and images; thinking; tool runs, one icon per run (`<slicc-tool-runs>`): three or more runs in a row form a cluster, a strip of icon buttons (`data-action="open-run"`, `data-target` the call's id, named by its `title` and status, `aria-expanded`) that each open one run's card below it, and on the right the model's label for the run, a button (`data-action="open-cluster"`, `aria-expanded`, named "Show all runs" while it has no text) that opens every card; the strip's tooltip and the group's accessible name sum it up, such as "3 commands, 1 read. Done"; prose breaks a run, hidden thinking and source links don't, and a standalone tool message or a done reply with only tool calls continues the run before it on the same day, its `data-id` moving onto its first card (`.tool[data-id]`); a single run is a strip of one, its icon and its `title` as the label, which opens with <kbd>Enter</kbd> or <kbd>Space</kbd> like the strip's buttons. A strip and its cards share one width, the column less a wide margin on each side, so they read as centred, and their edges line up. Icons fill from the start of a space kept for five, so a run that grows doesn't move its strip or its label; past five, the strip shows the newest five and scrolls back to the older ones, with <kbd>Tab</kbd> or a swipe, and returns to the newest when a run is added or the strip comes back into view. The label sits at the end and is cut with an ellipsis before it reaches that space. Under 480 px of column width the label takes a row of its own under the icons, which then show as many whole icons as fit. A cluster's label comes from the agent port's optional `toolLabel(agentId, callIds)`, asked once for each agent port, agent and set of runs; while a growing run waits for its new label, it keeps its last one, and without `toolLabel` the label is empty. A command's icon is the favicon of the site it names when an open browser tab already has a `data:` favicon for it (nothing is fetched; otherwise the globe), else the file browser's icon for the file it names, else the code icon; other tools show their kind's icon. A running run pulses (still, with a dashed ring, under `prefers-reduced-motion`), a done or cancelled one is grey and a failed one red, each with a corner glyph. The card (`.tool[data-tool][data-status][data-kind]`, a `details.run` under a pill and a `div[role=region]` named by its `title` and status under a strip) has no header of its own, no chevron, title, tool name or status: its input (a shell command after `$`), an edit's `diff` shown as a diff view, its output wrapped rather than cut off, an `image`, such as a screenshot, an optional `meta` such as a timeout, a standalone tool message's time ("Ran at 16:12", `.ran`, by the two-minute rule), and its `notes` (`{ severity, text }`, such as "Exited with code 5" or "Full output in /tmp/…") as lines under the output, `li[data-note][data-severity]`: `error` with an alert icon in the negative colour, `warn` with an alert icon and `info` with an info icon, both subdued; media galleries with images, video and audio; action cards for tools, pull requests and status; plans and checklists; inline diffs; question prompts; delegations; link previews; errors with a fix-it action, as a grey card (`.error-card[role=alert]`, `data-id` on system errors) inset by a wide margin on each side, a narrower one under 480 px: a red alert icon, the title in bold, the detail subdued and the action on the right, or under the detail below 360 px, announced as "Error: " and the title; a content-filter stop offers **Drop the last turn**, which rewinds that turn and puts its prompt back into the composer; when the turns right before it failed too, the button reads **Drop N failed turns** and rewinds to before the first of them, putting the latest prompt back), system messages (error cards; compactions and notices, rewinds and model changes included, as full-width dividers with a line on each side, like the day: a compaction shows only its title, its `text` kept for screen readers, one that lost messages (`state: 'fallback'`, by default "Compaction failed, transcript truncated", or `discarded`, "Context discarded") an alert icon in the negative colour, and a notice its title and text on one line, `role="note"`; a divider that wraps keeps a short line on each side) and licks, SLICC's events into a cone, on every channel, with their time by the two-minute rule; a lick's `severity` (`warn` or `error`) turns its channel badge into a notice or negative one with an alert icon. Permission requests are allowed or denied in place. The header is one row: the status, 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. 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. The pickers re-read `models()` on `settings`, `accounts` and `agents`. 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. The chat re-runs this on `follow` and `readOnly` too, and `<slicc-app>` does it as well, when it binds, on those events and on `follow` and `readOnly`, so a new cone that starts on a provider that isn't set up moves at once. The transcript pads its end, and its `scroll-padding-block-end`, by the composer's measured height, so at the bottom the last message sits above the composer and a focused element scrolls clear of it. While a reply streams, its status icon follows what it is doing: a pencil for an edit, a page for a read, a magnifier for a search, an eye for the browser and an hourglass for any other tool; thinking and text keep the AI logo. Between the header and the transcript, a `<slicc-notices>` shows the model's notices. On a follower without Control, or while the agent port's `readOnly()` returns a line, a note (`p[data-follow="watch"]`, that line first, else the follow-based one) takes the composer's place, the header shows the model's name instead of the pickers and New conversation, and there's no suggested follow-up. |
140
+ | `<slicc-chat>` | One thread's conversation (its `agent`, or the active agent if unset), by day. A message's time shows only when it is at least two minutes newer than the last time shown, and the first one after each day separator always shows; a hidden time stays for screen readers (`time.sr` with `datetime`, or `.sr` on licks and cards), and a meta row left empty isn't rendered. User messages (with attachments and a quiet timestamp, `time.when`, by that rule; the reader's own carry no name or delivery tag, while `data-mode` and `data-delivered` still say how they went; a message from another cone, a follower or a guest says who sent it, and an agent's request carries its name and an Agent badge; a message from another agent, an agent's request or a message `from` a cone (`origin` `agent`, or `from` set without `origin` `follower` or `guest`), is `[data-remote]`: a subtle indigo bubble and a 🤖 pill on its bottom edge, `aria-hidden` since the byline names the sender; people, the reader included, never get it), assistant messages (with the time by that rule, and the model only on the first reply and whenever it changes from the previous one, `.meta .model` with the usage as its tooltip; without a model the usage is the time's tooltip, and with neither it is the reply's own (`.reply[title]`); the turn always carries the usage as its `aria-description`; the cone's name isn't repeated; Markdown with tables, task lists, quotes, code and images; thinking; tool runs, one icon per run (`<slicc-tool-runs>`): three or more runs in a row form a cluster, a strip of icon buttons (`data-action="open-run"`, `data-target` the call's id, named by its `title` and status, `aria-expanded`) that each open one run's card below it, and on the right the model's label for the run, a button (`data-action="open-cluster"`, `aria-expanded`, named "Show all runs" while it has no text) that opens every card; the strip's tooltip and the group's accessible name sum it up, such as "3 commands, 1 read. Done"; prose breaks a run, hidden thinking and source links don't, and a standalone tool message or a done reply with only tool calls continues the run before it on the same day, its `data-id` moving onto its first card (`.tool[data-id]`); a single run is a strip of one, its icon and its `title` as the label, which opens with <kbd>Enter</kbd> or <kbd>Space</kbd> like the strip's buttons. A strip and its cards share one width, the column less a wide margin on each side, so they read as centred, and their edges line up. Icons fill from the start of a space kept for five, so a run that grows doesn't move its strip or its label; past five, the strip shows the newest five and scrolls back to the older ones, with <kbd>Tab</kbd> or a swipe, and returns to the newest when a run is added or the strip comes back into view. The label sits at the end and is cut with an ellipsis before it reaches that space. Under 480 px of column width the label takes a row of its own under the icons, which then show as many whole icons as fit. A cluster's label comes from the agent port's optional `toolLabel(agentId, callIds)`, asked once for each agent port, agent and set of runs; while a growing run waits for its new label, it keeps its last one, and without `toolLabel` the label is empty. A command's icon is the favicon of the site it names when an open browser tab already has a `data:` favicon for it (nothing is fetched; otherwise the globe), else the file browser's icon for the file it names, else the code icon; other tools show their kind's icon. A running run pulses (still, with a dashed ring, under `prefers-reduced-motion`), a done or cancelled one is grey and a failed one red, each with a corner glyph. The card (`.tool[data-tool][data-status][data-kind]`, a `details.run` under a pill and a `div[role=region]` named by its `title` and status under a strip) has no header of its own, no chevron, title, tool name or status: its input (a shell command after `$`), an edit's `diff` shown as a diff view, its output wrapped rather than cut off, an `image`, such as a screenshot, an optional `meta` such as a timeout, a standalone tool message's time ("Ran at 16:12", `.ran`, by the two-minute rule), and its `notes` (`{ severity, text }`, such as "Exited with code 5" or "Full output in /tmp/…") as lines under the output, `li[data-note][data-severity]`: `error` with an alert icon in the negative colour, `warn` with an alert icon and `info` with an info icon, both subdued; media galleries with images, video and audio; action cards for tools, pull requests and status; plans and checklists; inline diffs; question prompts; delegations; link previews; errors with a fix-it action, as a grey card (`.error-card[role=alert]`, `data-id` on system errors) inset by a wide margin on each side, a narrower one under 480 px: a red alert icon, the title in bold, the detail subdued and the action on the right, or under the detail below 360 px, announced as "Error: " and the title; a content-filter stop offers **Drop the last turn**, which rewinds that turn and puts its prompt back into the composer; when the turns right before it failed too, the button reads **Drop N failed turns** and rewinds to before the first of them, putting the latest prompt back), system messages (error cards; compactions and notices, rewinds and model changes included, as full-width dividers with a line on each side, like the day: a compaction shows only its title, its `text` kept for screen readers, one that lost messages (`state: 'fallback'`, by default "Compaction failed, transcript truncated", or `discarded`, "Context discarded") an alert icon in the negative colour, and a notice its title and text on one line, `role="note"`; a divider that wraps keeps a short line on each side) and licks, SLICC's events into a cone, on every channel, on the right (`.lick-turn`): a byline with the title and the time by the two-minute rule, then a dashed box (`.lick[data-id][data-channel]`) with the text and the count (×3); emoji pills on its bottom edge (`.reactions .reaction`, `aria-hidden`, each named by its `title`) give the channel (⏰ Schedule, 🪝 Webhook, ✨ Sprinkle, …) and the outcome (👍 Allowed, 👎 Denied, also said in the box's hidden text). A lick with a body is a `details.lick` whose head (`summary.lick-head`, with a quiet chevron that turns when open) toggles it with a click, <kbd>Enter</kbd> or <kbd>Space</kbd>; clicking the body doesn't close it. A lick's `severity` (`warn` or `error`) adds an alert icon to its byline, a coloured edge to its box and a spoken prefix. Permission requests are allowed or denied in place. The header is one row: the status, 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. 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. The pickers re-read `models()` on `settings`, `accounts` and `agents`. 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. The chat re-runs this on `follow` and `readOnly` too, and `<slicc-app>` does it as well, when it binds, on those events and on `follow` and `readOnly`, so a new cone that starts on a provider that isn't set up moves at once. The transcript pads its end, and its `scroll-padding-block-end`, by the composer's measured height, so at the bottom the last message sits above the composer and a focused element scrolls clear of it. While a reply streams, its status icon follows what it is doing: a pencil for an edit, a page for a read, a magnifier for a search, an eye for the browser and an hourglass for any other tool; thinking and text keep the AI logo. Between the header and the transcript, a `<slicc-notices>` shows the model's notices. On a follower without Control, or while the agent port's `readOnly()` returns a line, a note (`p[data-follow="watch"]`, that line first, else the follow-based one) takes the composer's place, the header shows the model's name instead of the pickers and New conversation, and there's no suggested follow-up. |
141
141
  | `<slicc-notices>` | The model's `notices` as in-line alerts with their actions and a close button (none with `dismissible: false`), after a banner from the `tabs` port when another tab is paused or runs a different SLICC, and a warning for each waiting approval when the `approvals` port has no `decide` (`[data-approval-notice]`); renders nothing without any. |
142
142
  | `<slicc-approvals>` | The model's waiting `approvals` as a modal alert dialog, one at a time, oldest first; `<slicc-app>` mounts it. Renders nothing without `approvals.decide`. A request that `secretRules()` flags shows a warning (`[data-secret-rules]`, the path or token) and no **Always allow (**Always allow in this directory**, with the cwd in its hint, when the approval has `alwaysScope: 'cwd'` and a `cwd`)**. Focus starts on Deny and goes back, after Deny, Always allow, Allow once, <kbd>Esc</kbd> or Close, to what had it when the dialog opened; inside a terminal, to that `<slicc-terminal>` through its own `focus()`, even when it remounted meanwhile. |
143
143
  | `<slicc-follow>` | The model's `follow` state. By default a header chip (`#chip[data-follow]` with the link state: **Connecting to**, **Following**, **Reconnecting to**, **Unlinked from** or **Can’t reach** the owner; `compact` keeps only a follow icon and the light) whose popover says what this device can do (`[data-can]`), that the conversation may be out of date while reconnecting, and with Control that sending waits (`[data-reconnecting]`), and has **Leave** (`data-action="leave"`, behind a confirm unless unlinked). With `page`, the full page for `connecting`, `waiting` (connected without View: "Waiting for {owner} to allow this device"), `unlinked` and `failed` (`[data-follow-page]`), with **Try again** (`data-action="retry"`) when failed and the port has `retry`, and **Leave**. A rejected call shows its message (`[data-error]`). Renders nothing without the port. |
package/dist/slicc-ui.js CHANGED
@@ -31012,7 +31012,7 @@ function user(message, stamp = true) {
31012
31012
  ${byline(message, who2, stamp)}${stamp ? A : unseen(message.createdAt)}
31013
31013
  ${message.text ? b`<swc-user-message class="body"><span class="text">${message.text}</span></swc-user-message>` : A}
31014
31014
  ${message.attachments?.length ? b`<div class="attachments">${message.attachments.map(attachment)}</div>` : A}
31015
- ${remote ? b`<span class="reaction" aria-hidden="true">🤖</span>` : A}
31015
+ ${remote ? b`<span class="reactions"><span class="reaction" aria-hidden="true">🤖</span></span>` : A}
31016
31016
  </div>
31017
31017
  </swc-conversation-turn>`;
31018
31018
  }
@@ -31110,6 +31110,9 @@ var compactionTitles = {
31110
31110
  function event(label, content) {
31111
31111
  return b`<swc-conversation-turn type="system" class="event" accessible-label=${label}>${content}</swc-conversation-turn>`;
31112
31112
  }
31113
+ function outside(label, byline2, content) {
31114
+ return b`<swc-conversation-turn type="user" class="event lick-turn" accessible-label=${label}><div class="bubble">${byline2}${content}</div></swc-conversation-turn>`;
31115
+ }
31113
31116
  function system(message, handlers) {
31114
31117
  if (message.kind === "compaction") {
31115
31118
  const failed2 = message.state === "fallback" || message.state === "discarded";
@@ -31158,37 +31161,57 @@ var severities = {
31158
31161
  icon: b`<swc-icon-alert-triangle slot="icon" size="s" aria-hidden="true"></swc-icon-alert-triangle>`
31159
31162
  }
31160
31163
  };
31161
- function severityBadge(message, label) {
31162
- if (!message.severity) return null;
31163
- const { prefix, variant, icon } = severities[message.severity];
31164
- return b`<span class="sr">${prefix}</span><swc-badge class="severity" size="s" variant=${variant} subtle>${icon}${label}</swc-badge>`;
31165
- }
31164
+ var lickIcons = {
31165
+ webhook: "\u{1FA9D}",
31166
+ cron: "\u23F0",
31167
+ sprinkle: "\u2728",
31168
+ fswatch: "\u{1F440}",
31169
+ "session-reload": "\u{1F504}",
31170
+ navigate: "\u{1F9ED}",
31171
+ discovery: "\u{1F50E}",
31172
+ upgrade: "\u2B06\uFE0F",
31173
+ workflow: "\u{1F4CB}",
31174
+ bash: "\u{1F4BB}",
31175
+ jshd: "\u{1F4DC}",
31176
+ preview: "\u{1F441}\uFE0F",
31177
+ cherry: "\u{1F352}",
31178
+ "scoop-notify": "\u2705",
31179
+ "scoop-idle": "\u{1F4A4}",
31180
+ "scoop-wait": "\u23F3",
31181
+ "sudo-request": "\u{1F510}"
31182
+ };
31166
31183
  function lick(message, handlers, stamp = true) {
31167
- const [label, variant] = lickLabels[message.channel];
31184
+ const [label] = lickLabels[message.channel];
31168
31185
  const decide = message.channel === "sudo-request" && message.state === "pending";
31169
31186
  const outcome = message.state === "confirmed" ? "Allowed" : message.state === "dismissed" ? "Denied" : "";
31170
- const head2 = b`${severityBadge(message, label) ?? b`<swc-badge class="channel" size="s" variant=${variant} subtle>${label}</swc-badge>`}
31171
- <strong>${message.title}</strong>
31187
+ const head2 = b`${message.severity ? b`<span class="sr">${severities[message.severity].prefix}</span>` : A}
31188
+ ${outcome ? b`<span class="sr">${outcome}. </span>` : A}
31172
31189
  <span class="text">${message.text}</span>
31173
- ${message.count && message.count > 1 ? b`<span class="times" title="Coalesced events">×${message.count}</span>` : A}
31174
- ${outcome ? b`<swc-badge class="tag" size="s" variant=${message.state === "confirmed" ? "positive" : "neutral"} subtle>${outcome}</swc-badge>` : A}
31175
- <span class=${stamp ? "when" : "sr"}>${time(message.createdAt)}</span>`;
31190
+ ${message.count && message.count > 1 ? b`<span class="times" title="Coalesced events">×${message.count}</span>` : A}`;
31191
+ const outcomeIcon = message.state === "confirmed" ? "\u{1F44D}" : message.state === "dismissed" ? "\u{1F44E}" : "";
31192
+ const reaction = b`<span class="reactions"><span class="reaction" title=${label} aria-hidden="true">${lickIcons[message.channel]}</span>${outcomeIcon ? b`<span class="reaction" title=${outcome} aria-hidden="true">${outcomeIcon}</span>` : A}</span>`;
31176
31193
  const actions2 = decide ? b`<div class="decide">
31177
31194
  <swc-button size="s" variant="accent" data-action="allow" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "confirmed")}>Allow</swc-button>
31178
31195
  <swc-button size="s" variant="secondary" fill-style="outline" data-action="deny" ?disabled=${handlers.readOnly} @click=${() => handlers.resolve(message.id, "dismissed")}>Deny</swc-button>
31179
31196
  </div>` : A;
31180
31197
  const name = `${message.severity ? severities[message.severity].prefix : ""}${label} event`;
31198
+ const byline2 = b`<div class="meta">${message.severity ? b`<span class="severity-icon">${severities[message.severity].icon}</span>` : A}<span class="who">${message.title}</span>${stamp ? when(message.createdAt) : unseen(message.createdAt)}</div>`;
31181
31199
  if (!message.body) {
31182
- return event(
31200
+ return outside(
31183
31201
  name,
31184
- b`<div class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}><div class="lick-head">${head2}</div>${actions2}</div>`
31202
+ byline2,
31203
+ b`<div class="lick-frame"><div class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}><div class="lick-head">${head2}</div>${actions2}</div>${reaction}</div>`
31185
31204
  );
31186
31205
  }
31187
31206
  const details = b`<details class="lick" data-id=${message.id} data-channel=${message.channel} data-severity=${message.severity ?? A}>
31188
- <summary class="lick-head"><span class="chevron" aria-hidden="true">▸</span>${head2}</summary>
31207
+ <summary class="lick-head">${head2}<swc-icon-chevron-right class="chevron" size="s" aria-hidden="true"></swc-icon-chevron-right></summary>
31189
31208
  <pre class="output">${message.body}</pre>
31190
31209
  </details>`;
31191
- return event(name, decide ? b`<div class="pending">${details}${actions2}</div>` : details);
31210
+ return outside(
31211
+ name,
31212
+ byline2,
31213
+ b`<div class="lick-frame">${decide ? b`<div class="pending">${details}${actions2}</div>` : details}${reaction}</div>`
31214
+ );
31192
31215
  }
31193
31216
  var messageCss = i`
31194
31217
  swc-conversation-thread > .message,
@@ -31258,10 +31281,14 @@ var messageCss = i`
31258
31281
  position: relative;
31259
31282
  margin-block-end: var(--swc-spacing-300);
31260
31283
  }
31261
- .reaction {
31284
+ .reactions {
31262
31285
  position: absolute;
31263
31286
  inset-block-end: calc(var(--swc-spacing-300) * -1);
31264
31287
  inset-inline-end: var(--swc-spacing-200);
31288
+ display: inline-flex;
31289
+ gap: var(--swc-spacing-50);
31290
+ }
31291
+ .reaction {
31265
31292
  display: inline-flex;
31266
31293
  align-items: center;
31267
31294
  justify-content: center;
@@ -31408,9 +31435,9 @@ var messageCss = i`
31408
31435
  }
31409
31436
  .lick {
31410
31437
  border: var(--swc-border-width-100) solid var(--swc-gray-200);
31411
- border-radius: var(--swc-corner-radius-100);
31438
+ border-radius: var(--swc-corner-radius-700);
31412
31439
  background: var(--swc-background-layer-1-color);
31413
- font-size: var(--swc-font-size-100);
31440
+ font-size: var(--swc-font-size-200);
31414
31441
  }
31415
31442
  summary,
31416
31443
  .lick-head {
@@ -31941,15 +31968,25 @@ var messageCss = i`
31941
31968
  color: var(--swc-neutral-subdued-content-color-default);
31942
31969
  text-align: center;
31943
31970
  }
31971
+ .lick-frame {
31972
+ position: relative;
31973
+ min-inline-size: 0;
31974
+ max-inline-size: 100%;
31975
+ margin-block-end: var(--swc-spacing-300);
31976
+ }
31944
31977
  .lick {
31945
31978
  border-style: dashed;
31946
31979
  }
31947
- .channel,
31948
- .lick .severity {
31949
- flex: none;
31980
+ .severity-icon {
31981
+ display: inline-flex;
31982
+ align-self: center;
31983
+ color: var(--severity);
31950
31984
  }
31951
- .lick strong {
31952
- white-space: nowrap;
31985
+ .lick-turn:has(.lick[data-severity='error']) {
31986
+ --severity: var(--swc-negative-visual-color);
31987
+ }
31988
+ .lick-turn:has(.lick[data-severity='warn']) {
31989
+ --severity: var(--swc-notice-visual-color);
31953
31990
  }
31954
31991
  .lick[data-severity] {
31955
31992
  border-inline-start: var(--swc-border-width-400) solid var(--severity);
@@ -31968,8 +32005,7 @@ var messageCss = i`
31968
32005
  clip-path: inset(50%);
31969
32006
  white-space: nowrap;
31970
32007
  }
31971
- .lick .times,
31972
- .lick .when {
32008
+ .lick .times {
31973
32009
  color: var(--swc-neutral-subdued-content-color-default);
31974
32010
  white-space: nowrap;
31975
32011
  }
@@ -31980,7 +32016,7 @@ var messageCss = i`
31980
32016
  }
31981
32017
  .pending {
31982
32018
  border: var(--swc-border-width-100) solid var(--swc-notice-visual-color);
31983
- border-radius: var(--swc-corner-radius-100);
32019
+ border-radius: var(--swc-corner-radius-700);
31984
32020
  }
31985
32021
  .pending > details.lick {
31986
32022
  border: 0;
@@ -80171,7 +80207,7 @@ function createDismissableLayer(options) {
80171
80207
  const capture = (_options$capture = options.capture) !== null && _options$capture !== void 0 ? _options$capture : false;
80172
80208
  const escape2 = (_options$escape = options.escape) !== null && _options$escape !== void 0 ? _options$escape : true;
80173
80209
  const keys = (_options$keys = options.keys) !== null && _options$keys !== void 0 ? _options$keys : [];
80174
- const outside = (_options$outsidePoint = options.outsidePointerDown) !== null && _options$outsidePoint !== void 0 ? _options$outsidePoint : true;
80210
+ const outside2 = (_options$outsidePoint = options.outsidePointerDown) !== null && _options$outsidePoint !== void 0 ? _options$outsidePoint : true;
80175
80211
  const grace = (_options$pointerDownG = options.pointerDownGraceMs) !== null && _options$pointerDownG !== void 0 ? _options$pointerDownG : 0;
80176
80212
  const now = (_options$now = options.now) !== null && _options$now !== void 0 ? _options$now : Date.now;
80177
80213
  const openedAt = now();
@@ -80184,13 +80220,13 @@ function createDismissableLayer(options) {
80184
80220
  if (escape2 || keys.length > 0) disposables.addDisposables(addDisposableListener(win, "keydown", (event2) => {
80185
80221
  if (escape2 && event2.key === "Escape" || keys.includes(event2.key)) options.onDismiss();
80186
80222
  }, capture));
80187
- if (outside || options.onInsidePointerDown) disposables.addDisposables(addDisposableListener(win, "pointerdown", (event2) => {
80223
+ if (outside2 || options.onInsidePointerDown) disposables.addDisposables(addDisposableListener(win, "pointerdown", (event2) => {
80188
80224
  if (isInside(event2)) {
80189
80225
  var _options$onInsidePoin;
80190
80226
  (_options$onInsidePoin = options.onInsidePointerDown) === null || _options$onInsidePoin === void 0 || _options$onInsidePoin.call(options, event2);
80191
80227
  return;
80192
80228
  }
80193
- if (!outside || now() - openedAt < grace) return;
80229
+ if (!outside2 || now() - openedAt < grace) return;
80194
80230
  options.onDismiss();
80195
80231
  }, capture));
80196
80232
  if (options.resize) disposables.addDisposables(addDisposableListener(win, "resize", () => {