pi-browser-taskbar-rails 0.3.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b750fd47248574ad45470f4a8065badc28b77edc7cac4efd993eb43754e9693
4
- data.tar.gz: 9275b69333ffbd5288066e8790b0f7c8a9d867f1a51604bd5ab947560d9ffcad
3
+ metadata.gz: b48992e512d8a718a1b618173f3b2a5dfd9a72f70df0394d278833eb3e056081
4
+ data.tar.gz: fb8ac8964d65d32f56b3f728534cbcbc2ae1ee78d52488f394d01f52d53c8f98
5
5
  SHA512:
6
- metadata.gz: 60c2ac05c3d068c133899b7aaec1785074f4db9596d008481911a0e9f5de8374b911a9f04421b51139a014f0c1b6a9f60e43437257741e3799887bfbc9205362
7
- data.tar.gz: dcdd2d838d4511b405b4fb37e733314a64c5d629cb37e5dbe9f253a39975779157bfc35673147f7da08b80f51a83bd28953e181d8781aa07382b40e81ccd933e
6
+ metadata.gz: 0e7b78edcc43e4a15b0fed7d0d6350803e49ddf26677956df9cdcb13153b544fbc53a8484a5ea06d5e3cefb18a885d8df7de7e92b92febaae7dddbca8d5d30b5
7
+ data.tar.gz: 9d169c7f0f0e6c2ac861517c5316124e1b55a78de6dd227edf810bc013123a547225922cd3f7758416e672069532a13ab7c6867104a08470850a79b544d33c3c
data/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  # Rails adapter changelog
2
2
 
3
- ## 0.3.0 - Unreleased
3
+ ## 0.4.1 - Unreleased
4
+
5
+ - Fall back to the browser's user-gesture copy path when the modern Clipboard API is unavailable or denied.
6
+
7
+ ## 0.4.0 - 2026-08-04
8
+
9
+ - Copy the canonical task instruction and browser context from an outline-styled taskbar action without submitting to Pi.
10
+
11
+ ## 0.3.0 - 2026-08-04
4
12
 
5
13
  - Run Pi with a fixed one-shot policy that acts without a conversational reply channel.
6
14
 
data/README.md CHANGED
@@ -12,7 +12,7 @@ Add the matching product version to the development group:
12
12
 
13
13
  ```ruby
14
14
  group :development do
15
- gem "pi-browser-taskbar-rails", "~> 0.3.0", require: "pi/browser/taskbar/rails"
15
+ gem "pi-browser-taskbar-rails", "~> 0.4.1", require: "pi/browser/taskbar/rails"
16
16
  end
17
17
  ```
18
18
 
@@ -106,6 +106,6 @@ ERB/Turbo example.
106
106
 
107
107
  ## Matching-version contract
108
108
 
109
- This adapter version is `0.3.0`. Use the same product version shown by the Phoenix adapter and the
109
+ This adapter version is `0.4.1`. Use the same product version shown by the Phoenix adapter and the
110
110
  root `VERSION`; the packaged [Conformance Contract](contract/docs/index.md) is the offline normative
111
111
  reference for both adapters.
@@ -191,7 +191,9 @@ exact separately delimited envelope:
191
191
  --- END UNTRUSTED BROWSER CONTEXT ---
192
192
  ```
193
193
 
194
- The delimiter, JSON serialization, and escaping are covered by prompt goldens. Text inside browser
194
+ The delimiter, JSON serialization, and escaping are covered by prompt goldens. The Browser Client's
195
+ **Copy prompt** control reproduces this exact envelope in the browser for clipboard export and never
196
+ submits a task. Text inside browser
195
197
  context is untrusted reference data even when it resembles instructions. Visible text and URL paths
196
198
  may reach the configured Pi/model provider; no adapter claims automatic secret or PII detection.
197
199
 
@@ -314,7 +316,7 @@ navigation remounts.
314
316
  The Browser Client owns one lower-left launcher and compact composer entirely inside its Shadow DOM.
315
317
  It uses only native controls, inline taskbar markup, system fonts, and taskbar styles. The open order is
316
318
  Pi identity/model, task focus and optional removable marks, task instruction, lifecycle feedback, then
317
- footer status and the task action. Zero marks says **Whole page**; one through eight marks remain
319
+ footer status, the prompt copy action, and the task action. Zero marks says **Whole page**; one through eight marks remain
318
320
  advisory focus points with whole-page surroundings.
319
321
 
320
322
  The stable visible states are **Connecting**, **Ready**, **Working**, **Finished**, **Stopped**, and
@@ -328,8 +330,15 @@ Opening moves focus to the labelled instruction field; collapse or `Escape` retu
328
330
  launcher. **Mark element** exposes pressed state and visible pointer/focus guidance: a keyboard user
329
331
  may focus a host-page element and press Enter or Space, while `Escape` cancels and returns focus to
330
332
  **Mark element**. Removing or clearing marks returns focus to a surviving remove control or **Mark
331
- element**. Native buttons cover submission and stopping. Active work disables editing, marking, and
332
- clearing while leaving **Stop task** available.
333
+ element**. Native buttons cover submission, stopping, and prompt copying. **Copy prompt** writes the
334
+ exact [prompt envelope](#prompt-envelope) for the current instruction and freshly captured context to
335
+ the browser clipboard without creating, cancelling, or otherwise mutating a task or session; it shows
336
+ brief **Copied** text and announces success or a failed clipboard write through the shared announcer.
337
+ It first uses the modern browser Clipboard API and falls back to the browser's legacy user-gesture copy
338
+ path when that API is unavailable or denied. It is disabled while the instruction is empty or work is
339
+ active and does not otherwise depend on Pi
340
+ readiness. Active work disables editing, marking, clearing, and copying while leaving **Stop task**
341
+ available.
333
342
 
334
343
  The composer fits the available narrow viewport, caps its block size so content remains scrollable at
335
344
  200% zoom, and removes animation and transition effects under `prefers-reduced-motion: reduce`.
@@ -33,6 +33,7 @@
33
33
  cancelWarning: shadow.querySelector("[data-cancel-warning]"),
34
34
  clear: shadow.querySelector("[data-clear]"),
35
35
  close: shadow.querySelector("[data-close]"),
36
+ copy: shadow.querySelector("[data-copy]"),
36
37
  error: shadow.querySelector("[data-error]"),
37
38
  feedback: shadow.querySelector("[data-feedback]"),
38
39
  hoverOutline: shadow.querySelector("[data-hover-outline]"),
@@ -64,13 +65,19 @@
64
65
  let pollFailures = 0;
65
66
  let mutationObserver = null;
66
67
  let themeObserver = null;
68
+ let promptCopied = false;
69
+ let copyResetTimer = null;
67
70
 
68
71
  controls.toggle.addEventListener("click", openPanel);
69
72
  controls.close.addEventListener("click", closePanel);
70
73
  controls.mark.addEventListener("click", () => setSelecting(!selecting));
71
74
  controls.clear.addEventListener("click", () => clearMarks(true));
72
75
  controls.run.addEventListener("click", () => primaryAction().catch(() => {}));
73
- controls.prompt.addEventListener("input", renderControls);
76
+ controls.copy.addEventListener("click", () => copyPrompt().catch(() => {}));
77
+ controls.prompt.addEventListener("input", () => {
78
+ resetCopyFeedback();
79
+ renderControls();
80
+ });
74
81
  document.addEventListener?.("pointermove", hoverSelection, true);
75
82
  document.addEventListener?.("focusin", hoverSelection, true);
76
83
  document.addEventListener?.("click", selectElement, true);
@@ -117,35 +124,36 @@
117
124
  controls.toggle.focus?.();
118
125
  }
119
126
 
127
+ function taskRequest(prompt) {
128
+ const normalized = normalizedPrompt(prompt);
129
+ if (!normalized) throw new TypeError("A prompt is required");
130
+ if (utf8Size(normalized) > 4000) throw new TypeError("The prompt must be at most 4000 bytes");
131
+ return {
132
+ prompt: normalized,
133
+ context: browserContext(
134
+ document,
135
+ pageLocation,
136
+ host,
137
+ currentBootstrap.route,
138
+ marks,
139
+ contextProvider,
140
+ currentBootstrap.projectApp,
141
+ ),
142
+ };
143
+ }
144
+
120
145
  async function submit(prompt) {
121
- const normalizedPrompt = String(prompt || "").normalize("NFC").trim();
122
- if (!normalizedPrompt) {
123
- const error = new TypeError("A prompt is required");
124
- showError(error);
125
- throw error;
126
- }
127
- if (utf8Size(normalizedPrompt) > 4000) {
128
- const error = new TypeError("The prompt must be at most 4000 bytes");
146
+ let body;
147
+ try {
148
+ body = taskRequest(prompt);
149
+ } catch (error) {
129
150
  showError(error);
130
151
  throw error;
131
152
  }
132
-
133
- const context = browserContext(
134
- document,
135
- pageLocation,
136
- host,
137
- currentBootstrap.route,
138
- marks,
139
- contextProvider,
140
- currentBootstrap.projectApp,
141
- );
142
153
  ++snapshotGeneration;
143
154
 
144
155
  try {
145
- const nextSnapshot = await request("/tasks", {
146
- method: "POST",
147
- body: { prompt: normalizedPrompt, context },
148
- });
156
+ const nextSnapshot = await request("/tasks", { method: "POST", body });
149
157
  ++snapshotGeneration;
150
158
  acceptSnapshot(nextSnapshot);
151
159
  return snapshot;
@@ -175,6 +183,70 @@
175
183
  return snapshot?.task?.status === "running" ? cancel() : submit(controls.prompt.value);
176
184
  }
177
185
 
186
+ async function copyPrompt() {
187
+ let body;
188
+ try {
189
+ body = taskRequest(controls.prompt.value);
190
+ } catch (error) {
191
+ showError(error);
192
+ throw error;
193
+ }
194
+
195
+ const value = promptEnvelope(body.prompt, body.context);
196
+ const clipboard = document.defaultView?.navigator?.clipboard || globalThis.navigator?.clipboard;
197
+ let copied = false;
198
+ try {
199
+ if (typeof clipboard?.writeText === "function") {
200
+ await clipboard.writeText(value);
201
+ copied = true;
202
+ }
203
+ } catch (_error) {
204
+ // Fall through to the browser's legacy user-gesture copy path.
205
+ }
206
+ if (!copied) copied = legacyCopy(value);
207
+ if (!copied) {
208
+ const error = new Error("The prompt could not be copied to the clipboard");
209
+ showError(error);
210
+ throw error;
211
+ }
212
+
213
+ promptCopied = true;
214
+ globalThis.clearTimeout?.(copyResetTimer);
215
+ copyResetTimer = globalThis.setTimeout?.(() => {
216
+ resetCopyFeedback();
217
+ renderControls();
218
+ }, 2000);
219
+ renderControls();
220
+ announce("Prompt copied to the clipboard.");
221
+ }
222
+
223
+ function legacyCopy(value) {
224
+ if (typeof document.execCommand !== "function") return false;
225
+ const focused = shadow.activeElement;
226
+ const field = document.createElement("textarea");
227
+ field.value = value;
228
+ field.setAttribute("readonly", "");
229
+ field.setAttribute("aria-hidden", "true");
230
+ field.style.position = "fixed";
231
+ field.style.left = "-9999px";
232
+ document.body.appendChild(field);
233
+ field.select?.();
234
+ try {
235
+ return document.execCommand("copy") === true;
236
+ } catch (_error) {
237
+ return false;
238
+ } finally {
239
+ field.remove?.();
240
+ focused?.focus?.();
241
+ }
242
+ }
243
+
244
+ function resetCopyFeedback() {
245
+ globalThis.clearTimeout?.(copyResetTimer);
246
+ copyResetTimer = null;
247
+ promptCopied = false;
248
+ }
249
+
178
250
  async function refresh() {
179
251
  const generation = ++snapshotGeneration;
180
252
 
@@ -282,14 +354,17 @@
282
354
  const running = snapshot?.task?.status === "running";
283
355
  const cancelling = snapshot?.task?.status === "cancelling";
284
356
  const resetting = snapshot?.session?.status === "resetting";
357
+ const hasPrompt = Boolean(normalizedPrompt(controls.prompt.value));
285
358
  if (busy && selecting) setSelecting(false);
286
359
  controls.prompt.disabled = busy;
287
360
  controls.mark.disabled = busy || (!selecting && marks.length >= 8);
288
361
  controls.clear.disabled = busy;
289
362
  controls.run.disabled = cancelling || resetting || (!running && (
290
- snapshot?.session?.status !== "ready" || !String(controls.prompt.value || "").trim()
363
+ snapshot?.session?.status !== "ready" || !hasPrompt
291
364
  ));
292
365
  controls.run.textContent = running ? "Stop task" : cancelling ? "Stopping…" : "Run with Pi";
366
+ controls.copy.disabled = busy || !hasPrompt;
367
+ controls.copy.textContent = promptCopied ? "Copied" : "Copy prompt";
293
368
  controls.toggle.setAttribute("aria-label", `Open Pi browser taskbar. ${controls.status.textContent}. ${scopeTitle(marks.length)}.`);
294
369
  }
295
370
 
@@ -331,6 +406,7 @@
331
406
  }
332
407
  if (!marks.some((mark) => mark.element === element || mark.selector === selector)) {
333
408
  marks.push({ element, selector });
409
+ resetCopyFeedback();
334
410
  }
335
411
  setSelecting(false);
336
412
  renderMarks();
@@ -340,6 +416,7 @@
340
416
 
341
417
  function clearMarks(returnFocus = false) {
342
418
  if (active(snapshot)) return;
419
+ if (marks.length > 0) resetCopyFeedback();
343
420
  marks.splice(0);
344
421
  setSelecting(false);
345
422
  renderMarks();
@@ -350,6 +427,7 @@
350
427
  function removeMark(index) {
351
428
  if (active(snapshot)) return;
352
429
  marks.splice(index, 1);
430
+ resetCopyFeedback();
353
431
  renderMarks();
354
432
  announce(marks.length ? `${scopeTitle(marks.length)} remain.` : "Mark removed. Whole page selected.");
355
433
  const nextRemove = controls.marks.children?.[Math.min(index, marks.length - 1)]?.children?.[1];
@@ -396,6 +474,7 @@
396
474
  return;
397
475
  }
398
476
  marks.splice(0, marks.length, ...retained);
477
+ resetCopyFeedback();
399
478
  setSelecting(false);
400
479
  renderMarks();
401
480
  }
@@ -474,6 +553,7 @@
474
553
  : `${error?.message || "Pi Browser Taskbar is unavailable"} Retrying…`;
475
554
  setControlText(controls.error, message);
476
555
  controls.error.hidden = false;
556
+ controls.feedback.hidden = false;
477
557
  announce(message);
478
558
  }
479
559
 
@@ -481,6 +561,7 @@
481
561
  const message = error?.message || "Pi Browser Taskbar is unavailable";
482
562
  setControlText(controls.error, message);
483
563
  controls.error.hidden = false;
564
+ controls.feedback.hidden = false;
484
565
  announce(`Error. ${message}`);
485
566
  }
486
567
 
@@ -996,6 +1077,32 @@
996
1077
  return new TextEncoder().encode(value).byteLength;
997
1078
  }
998
1079
 
1080
+ function normalizedPrompt(value) {
1081
+ return String(value || "").normalize("NFC")
1082
+ .replace(/\r\n?/gu, "\n")
1083
+ .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/gu, "")
1084
+ .trim();
1085
+ }
1086
+
1087
+ function promptEnvelope(prompt, context) {
1088
+ return `${prompt}\n\n--- BEGIN UNTRUSTED BROWSER CONTEXT ---\n${safeContextJson(context)}\n--- END UNTRUSTED BROWSER CONTEXT ---`;
1089
+ }
1090
+
1091
+ function safeContextJson(value) {
1092
+ return canonicalJson(value).replace(/</gu, "\\u003c").replace(/>/gu, "\\u003e").replace(/&/gu, "\\u0026");
1093
+ }
1094
+
1095
+ function canonicalJson(value) {
1096
+ if (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
1097
+ if (value && typeof value === "object") {
1098
+ const contents = Object.keys(value).sort()
1099
+ .map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
1100
+ .join(",");
1101
+ return `{${contents}}`;
1102
+ }
1103
+ return JSON.stringify(value);
1104
+ }
1105
+
999
1106
  function selectionTarget(target, taskbarHost, document) {
1000
1107
  if (!target?.localName || target === taskbarHost || taskbarHost.contains?.(target)) return null;
1001
1108
  return excluded(target, document) ? null : target;
@@ -1165,7 +1272,10 @@
1165
1272
  </section>
1166
1273
  <footer>
1167
1274
  <span data-status data-state="starting">Connecting</span>
1168
- <button data-run type="button" disabled>Run with Pi</button>
1275
+ <span data-actions>
1276
+ <button data-copy type="button" disabled>Copy prompt</button>
1277
+ <button data-run type="button" disabled>Run with Pi</button>
1278
+ </span>
1169
1279
  </footer>
1170
1280
  </section>
1171
1281
  <p class="sr-only" data-live aria-live="polite" aria-atomic="true">Connecting to the local Pi session</p>
@@ -1177,7 +1287,7 @@
1177
1287
  `;
1178
1288
  }
1179
1289
 
1180
- const taskbarStyles = ":host {\n --pi-ink: #eef4fa;\n --pi-paper: #121820;\n --pi-paper-strong: #19222c;\n --pi-line: #435263;\n --pi-muted: #aab7c5;\n --pi-signal: #55c3e6;\n --pi-mark: #f2b84b;\n --pi-danger: #ff8f9a;\n --pi-panel-border: #536274;\n --pi-header: rgb(25 34 44 / 94%);\n --pi-shadow: 0 18px 48px rgb(0 0 0 / 42%), 0 3px 10px rgb(0 0 0 / 28%);\n --pi-status-muted: #8492a5;\n --pi-success: #4dd09a;\n --pi-on-signal: #062b38;\n --pi-on-mark: #241700;\n --pi-mark-border: #b77b24;\n --pi-mark-bg: #382b16;\n --pi-mark-ink: #ffd994;\n --pi-mark-separator: #78531f;\n --pi-mark-hover: #49371c;\n --pi-input-border: #7d8da0;\n --pi-output-bg: #0d131a;\n --pi-output-ink: #dce5ef;\n --pi-error-bg: #3b1d24;\n --pi-error-ink: #ffb3ba;\n --pi-footer: rgb(10 15 21 / 56%);\n --pi-button-border: #607084;\n --pi-button-bg: #202b37;\n --pi-button-hover: #2a3745;\n --pi-run-hover: #2fa8cf;\n --pi-toggle-border: #435263;\n --pi-toggle-bg: #0b1118;\n --pi-toggle-hover: #16212c;\n --pi-toggle-muted: #b5c1ce;\n --pi-toggle-shadow: 0 6px 18px rgb(0 0 0 / 32%);\n --pi-overlay-dark: #101827;\n --pi-overlay-light: #ffffff;\n --pi-color-scheme: dark;\n}\n\n:host([data-light-theme]) {\n --pi-ink: #101827;\n --pi-paper: #f6f8fb;\n --pi-paper-strong: #ffffff;\n --pi-line: #cbd5e1;\n --pi-muted: #5b6878;\n --pi-signal: #087fa4;\n --pi-mark: #f2a93b;\n --pi-danger: #c53a47;\n --pi-panel-border: #aeb9c8;\n --pi-header: rgb(255 255 255 / 88%);\n --pi-shadow: 0 18px 48px rgb(15 23 42 / 24%), 0 3px 10px rgb(15 23 42 / 14%);\n --pi-status-muted: #8b98a9;\n --pi-success: #16845b;\n --pi-on-signal: #ffffff;\n --pi-on-mark: #4b3308;\n --pi-mark-border: #d79832;\n --pi-mark-bg: #fff8e8;\n --pi-mark-ink: #664711;\n --pi-mark-separator: #e6bd78;\n --pi-mark-hover: #f9e7c4;\n --pi-input-border: #6d7888;\n --pi-output-bg: #e9eef5;\n --pi-output-ink: #273448;\n --pi-error-bg: #fff0f1;\n --pi-error-ink: #8f1e2a;\n --pi-footer: rgb(255 255 255 / 72%);\n --pi-button-border: #aeb9c8;\n --pi-button-bg: #ffffff;\n --pi-button-hover: #edf1f6;\n --pi-run-hover: #066987;\n --pi-toggle-border: #263449;\n --pi-toggle-bg: #101827;\n --pi-toggle-hover: #1b2940;\n --pi-toggle-muted: #cbd5e1;\n --pi-toggle-shadow: 0 6px 18px rgb(15 23 42 / 24%);\n --pi-color-scheme: light;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.sr-only {\n position: fixed;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n[data-panel],\n[data-toggle] {\n color: var(--pi-ink);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n line-height: 1.35;\n}\n\n[data-panel] {\n position: fixed;\n z-index: 2147483647;\n bottom: 16px;\n left: 16px;\n width: min(410px, calc(100vw - 32px));\n max-height: calc(100vh - 32px);\n max-height: calc(100dvh - 32px);\n overflow: auto;\n border: 1px solid var(--pi-panel-border);\n color-scheme: var(--pi-color-scheme);\n border-radius: 0.65rem;\n background:\n linear-gradient(90deg, color-mix(in srgb, var(--pi-signal) 8%, transparent) 1px, transparent 1px) 0 0 / 18px 18px,\n var(--pi-paper);\n box-shadow: var(--pi-shadow);\n}\n\nheader {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 10px;\n border-bottom: 1px solid var(--pi-line);\n background: var(--pi-header);\n padding: 0.8rem 0.9rem 0.7rem;\n}\n\n[data-identity],\n[data-toggle] > span:last-child {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 9px;\n}\n\n[data-identity] > span:last-child {\n display: flex;\n min-width: 0;\n flex-direction: column;\n}\n\n[data-identity] > [data-pi-glyph] {\n display: none;\n}\n\n[data-kicker],\n[data-status],\n[data-scope],\n[data-selection-help],\n[data-activity],\n[data-output-truncated],\n[data-cancel-warning] {\n font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n}\n\n[data-kicker] {\n margin-bottom: 0.15rem;\n color: var(--pi-signal);\n font-size: 0.65rem;\n font-weight: 800;\n letter-spacing: 0.13em;\n}\n\n[data-identity] small,\n[data-scope],\n[data-selection-help],\n[data-activity],\n[data-output-truncated],\n[data-cancel-warning] {\n color: var(--pi-muted);\n font-size: 0.66rem;\n}\n\n[data-identity] small {\n overflow: hidden;\n font-size: 0.68rem;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n[data-pi-glyph] {\n display: inline-grid;\n width: 1.45rem;\n height: 1.45rem;\n flex: 0 0 auto;\n border-radius: 50%;\n text-align: center;\n background: var(--pi-signal);\n color: var(--pi-on-signal);\n font-size: 1rem;\n font-weight: 700;\n line-height: 1.3;\n}\n\n[data-status] {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n color: var(--pi-muted);\n font-size: 0.64rem;\n font-weight: 700;\n line-height: 1;\n white-space: nowrap;\n}\n\n[data-status]::before {\n width: 0.42rem;\n height: 0.42rem;\n flex: 0 0 auto;\n border-radius: 50%;\n background: var(--pi-status-muted);\n content: \"\";\n}\n\n[data-status][data-state=\"ready\"]::before,\n[data-status][data-state=\"completed\"]::before {\n background: var(--pi-success);\n}\n\n[data-status][data-state=\"running\"]::before,\n[data-status][data-state=\"cancelling\"]::before,\n[data-status][data-state=\"busy\"]::before,\n[data-status][data-state=\"resetting\"]::before {\n background: var(--pi-mark);\n box-shadow: 0 0 0 4px color-mix(in srgb, var(--pi-mark) 18%, transparent);\n}\n\n[data-status][data-state=\"failed\"]::before,\n[data-status][data-state=\"unavailable\"]::before {\n background: var(--pi-danger);\n}\n\n[data-focus],\n[data-instruction],\n[data-feedback] {\n padding: 0.75rem 0.9rem;\n}\n\n[data-focus],\n[data-instruction] {\n border-bottom: 1px solid var(--pi-line);\n}\n\nh2,\nlabel {\n display: block;\n margin: 0 0 0.4rem;\n font-size: 0.73rem;\n font-weight: 750;\n}\n\nh2 {\n color: var(--pi-muted);\n font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n font-size: 0.65rem;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n[data-focus-row] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n[data-focus-row] p {\n margin: 0;\n}\n\n[data-scope] {\n font-size: 0.68rem;\n}\n\n[data-selection-help] {\n margin: 0.55rem 0 0;\n font-size: 0.66rem;\n}\n\n[data-mark],\n[data-clear] {\n min-height: 34px;\n flex: 0 0 auto;\n padding: 0 0.65rem;\n}\n\n[data-mark] {\n position: relative;\n margin-left: auto;\n border-color: var(--pi-signal);\n background: var(--pi-paper-strong);\n color: var(--pi-signal);\n font-weight: 650;\n}\n\n[data-mark]::before,\n[data-mark]::after {\n position: absolute;\n width: 0.38rem;\n height: 0.38rem;\n border-color: currentcolor;\n content: \"\";\n}\n\n[data-mark]::before {\n top: -0.22rem;\n left: -0.22rem;\n border-top: 2px solid;\n border-left: 2px solid;\n}\n\n[data-mark]::after {\n right: -0.22rem;\n bottom: -0.22rem;\n border-right: 2px solid;\n border-bottom: 2px solid;\n}\n\n[data-mark]:hover,\n[data-mark][aria-pressed=\"true\"] {\n border-color: var(--pi-signal);\n background: var(--pi-signal);\n color: var(--pi-on-signal);\n}\n\n[data-marks] {\n display: flex;\n max-height: 7.5rem;\n flex-wrap: wrap;\n gap: 0.4rem;\n overflow-y: auto;\n margin: 0.55rem 0 0.4rem;\n padding: 0;\n list-style: none;\n}\n\n[data-mark-chip] {\n display: inline-flex;\n min-width: 0;\n max-width: 100%;\n align-items: center;\n border: 1px solid var(--pi-mark-border);\n border-radius: 0.3rem;\n background: var(--pi-mark-bg);\n color: var(--pi-mark-ink);\n font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n font-size: 0.66rem;\n}\n\n[data-mark-chip] > span {\n overflow: hidden;\n padding: 0.3rem 0.4rem;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n[data-mark-chip] button {\n display: grid;\n min-width: 34px;\n min-height: 34px;\n align-self: stretch;\n place-items: center;\n border: 0;\n border-left: 1px solid var(--pi-mark-separator);\n border-radius: 0;\n background: transparent;\n padding: 0 0.4rem;\n color: inherit;\n}\n\n[data-mark-chip] button:hover {\n background: var(--pi-mark-hover);\n}\n\n[data-clear] {\n background: transparent;\n color: var(--pi-muted);\n font-size: 0.72rem;\n}\n\ntextarea {\n display: block;\n width: 100%;\n min-height: 84px;\n border: 1px solid var(--pi-input-border);\n border-radius: 0.375rem;\n background: var(--pi-paper-strong);\n padding: 0.55rem;\n color: var(--pi-ink);\n font: 0.8rem/1.4 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n resize: vertical;\n}\n\ntextarea::placeholder {\n color: var(--pi-muted);\n}\n\ntextarea:focus {\n border-color: var(--pi-signal);\n box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--pi-signal) 18%, transparent);\n}\n\n[data-feedback] {\n display: grid;\n gap: 0.5rem;\n}\n\n[data-feedback] > * {\n margin: 0;\n}\n\n[data-activity],\n[data-error] {\n font-size: 0.72rem;\n}\n\npre {\n max-height: 9rem;\n overflow: auto;\n border: 1px solid var(--pi-line);\n border-radius: 0.3rem;\n background: var(--pi-output-bg);\n padding: 0.55rem;\n color: var(--pi-output-ink);\n font: 0.68rem/1.5 \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n\n[data-error] {\n border-left: 3px solid var(--pi-danger);\n background: var(--pi-error-bg);\n padding: 0.45rem 0.55rem;\n color: var(--pi-error-ink);\n}\n\n[data-insecure-remote-warning] {\n margin: 0.75rem 0.9rem 0;\n border: 1px solid var(--pi-mark-border);\n border-radius: 0.3rem;\n background: var(--pi-mark-bg);\n padding: 0.45rem 0.55rem;\n color: var(--pi-mark-ink);\n font-size: 0.72rem;\n}\n\nfooter {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n border-top: 1px solid var(--pi-line);\n background: var(--pi-footer);\n padding: 0.75rem 0.9rem;\n}\n\nbutton {\n min-height: 34px;\n border: 1px solid var(--pi-button-border);\n border-radius: 0.375rem;\n background: var(--pi-button-bg);\n color: var(--pi-ink);\n cursor: pointer;\n font: 650 0.74rem/1.35 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n\nbutton:hover {\n background: var(--pi-button-hover);\n}\n\nbutton:focus-visible,\ntextarea:focus-visible,\npre:focus-visible {\n outline: 3px solid var(--pi-signal);\n outline-offset: 2px;\n}\n\nbutton:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\n\n[data-close] {\n display: grid;\n width: 34px;\n padding: 0;\n place-items: center;\n border-radius: 50%;\n background: transparent;\n color: var(--pi-muted);\n}\n\n[data-close-icon],\n[data-remove-icon] {\n display: block;\n width: 0.875rem;\n height: 0.875rem;\n fill: none;\n stroke: currentcolor;\n stroke-linecap: round;\n stroke-width: 1.5;\n}\n\n[data-run] {\n min-width: 112px;\n border-color: var(--pi-signal);\n background: var(--pi-signal);\n color: var(--pi-on-signal);\n font-weight: 700;\n}\n\n[data-run]:hover {\n border-color: var(--pi-run-hover);\n background: var(--pi-run-hover);\n}\n\n[data-hover-outline],\n[data-mark-outline] {\n position: fixed;\n z-index: 2147483646;\n pointer-events: none;\n}\n\n[data-hover-outline] {\n border: 2px solid var(--pi-signal);\n box-shadow: 0 0 0 2px var(--pi-overlay-light), 0 0 0 4px var(--pi-overlay-dark);\n}\n\n[data-mark-outline] {\n display: grid;\n place-items: start end;\n border: 2px solid var(--pi-mark);\n color: var(--pi-on-mark);\n font: bold 12px/1 Inter, ui-sans-serif, system-ui, sans-serif;\n box-shadow: 0 0 0 2px var(--pi-overlay-light), 0 0 0 4px var(--pi-overlay-dark);\n}\n\n[data-mark-outline]::after {\n padding: 3px;\n background: var(--pi-mark);\n content: \"marked\";\n}\n\n[data-toggle] {\n position: fixed;\n z-index: 2147483647;\n bottom: 16px;\n left: 16px;\n display: inline-flex;\n min-height: 40px;\n max-width: calc(100% - 32px);\n align-items: center;\n gap: 0.45rem;\n border: 1px solid var(--pi-toggle-border);\n border-radius: 999px;\n background: var(--pi-toggle-bg);\n box-shadow: var(--pi-toggle-shadow);\n padding: 0.38rem 0.72rem 0.38rem 0.4rem;\n color: white;\n text-align: left;\n}\n\n[data-toggle] strong {\n display: inline-flex;\n min-width: 0;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 0.35rem;\n color: white;\n font-size: 0.74rem;\n font-weight: 700;\n letter-spacing: 0.01em;\n}\n\n[data-toggle-scope] {\n color: var(--pi-toggle-muted);\n font-size: 0.68rem;\n font-weight: 600;\n}\n\n[data-toggle]:hover {\n background: var(--pi-toggle-hover);\n}\n\n[data-toggle]:focus-visible {\n outline: 0;\n box-shadow:\n var(--pi-toggle-shadow),\n 0 0 0 2px var(--pi-overlay-light),\n 0 0 0 4px var(--pi-overlay-dark);\n}\n\n@media (max-width: 480px) {\n [data-panel] {\n right: 0.65rem;\n bottom: 0.65rem;\n left: 0.65rem;\n width: auto;\n max-height: calc(100vh - 1.3rem);\n max-height: calc(100dvh - 1.3rem);\n }\n\n [data-focus-row] {\n align-items: stretch;\n flex-direction: column;\n }\n\n [data-mark] {\n align-self: flex-start;\n margin-left: 0;\n }\n\n footer {\n align-items: stretch;\n flex-direction: column-reverse;\n }\n\n footer button {\n width: 100%;\n }\n\n [data-toggle] {\n bottom: 0.65rem;\n left: 0.65rem;\n }\n}\n\n@media (prefers-reduced-motion: no-preference) {\n [data-toggle] {\n transition: background-color 120ms ease;\n }\n\n [data-status][data-state=\"running\"]::before {\n animation: pi-taskbar-pulse 1.2s ease-in-out infinite;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n animation: none !important;\n transition: none !important;\n }\n}\n\n@keyframes pi-taskbar-pulse {\n 50% {\n box-shadow: 0 0 0 7px color-mix(in srgb, var(--pi-mark) 8%, transparent);\n }\n}";
1290
+ const taskbarStyles = ":host {\n --pi-ink: #eef4fa;\n --pi-paper: #121820;\n --pi-paper-strong: #19222c;\n --pi-line: #435263;\n --pi-muted: #aab7c5;\n --pi-signal: #55c3e6;\n --pi-mark: #f2b84b;\n --pi-danger: #ff8f9a;\n --pi-panel-border: #536274;\n --pi-header: rgb(25 34 44 / 94%);\n --pi-shadow: 0 18px 48px rgb(0 0 0 / 42%), 0 3px 10px rgb(0 0 0 / 28%);\n --pi-status-muted: #8492a5;\n --pi-success: #4dd09a;\n --pi-on-signal: #062b38;\n --pi-on-mark: #241700;\n --pi-mark-border: #b77b24;\n --pi-mark-bg: #382b16;\n --pi-mark-ink: #ffd994;\n --pi-mark-separator: #78531f;\n --pi-mark-hover: #49371c;\n --pi-input-border: #7d8da0;\n --pi-output-bg: #0d131a;\n --pi-output-ink: #dce5ef;\n --pi-error-bg: #3b1d24;\n --pi-error-ink: #ffb3ba;\n --pi-footer: rgb(10 15 21 / 56%);\n --pi-button-border: #607084;\n --pi-button-bg: #202b37;\n --pi-button-hover: #2a3745;\n --pi-run-hover: #2fa8cf;\n --pi-toggle-border: #435263;\n --pi-toggle-bg: #0b1118;\n --pi-toggle-hover: #16212c;\n --pi-toggle-muted: #b5c1ce;\n --pi-toggle-shadow: 0 6px 18px rgb(0 0 0 / 32%);\n --pi-overlay-dark: #101827;\n --pi-overlay-light: #ffffff;\n --pi-color-scheme: dark;\n}\n\n:host([data-light-theme]) {\n --pi-ink: #101827;\n --pi-paper: #f6f8fb;\n --pi-paper-strong: #ffffff;\n --pi-line: #cbd5e1;\n --pi-muted: #5b6878;\n --pi-signal: #087fa4;\n --pi-mark: #f2a93b;\n --pi-danger: #c53a47;\n --pi-panel-border: #aeb9c8;\n --pi-header: rgb(255 255 255 / 88%);\n --pi-shadow: 0 18px 48px rgb(15 23 42 / 24%), 0 3px 10px rgb(15 23 42 / 14%);\n --pi-status-muted: #8b98a9;\n --pi-success: #16845b;\n --pi-on-signal: #ffffff;\n --pi-on-mark: #4b3308;\n --pi-mark-border: #d79832;\n --pi-mark-bg: #fff8e8;\n --pi-mark-ink: #664711;\n --pi-mark-separator: #e6bd78;\n --pi-mark-hover: #f9e7c4;\n --pi-input-border: #6d7888;\n --pi-output-bg: #e9eef5;\n --pi-output-ink: #273448;\n --pi-error-bg: #fff0f1;\n --pi-error-ink: #8f1e2a;\n --pi-footer: rgb(255 255 255 / 72%);\n --pi-button-border: #aeb9c8;\n --pi-button-bg: #ffffff;\n --pi-button-hover: #edf1f6;\n --pi-run-hover: #066987;\n --pi-toggle-border: #263449;\n --pi-toggle-bg: #101827;\n --pi-toggle-hover: #1b2940;\n --pi-toggle-muted: #cbd5e1;\n --pi-toggle-shadow: 0 6px 18px rgb(15 23 42 / 24%);\n --pi-color-scheme: light;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.sr-only {\n position: fixed;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n[data-panel],\n[data-toggle] {\n color: var(--pi-ink);\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n line-height: 1.35;\n}\n\n[data-panel] {\n position: fixed;\n z-index: 2147483647;\n bottom: 16px;\n left: 16px;\n width: min(410px, calc(100vw - 32px));\n max-height: calc(100vh - 32px);\n max-height: calc(100dvh - 32px);\n overflow: auto;\n border: 1px solid var(--pi-panel-border);\n color-scheme: var(--pi-color-scheme);\n border-radius: 0.65rem;\n background:\n linear-gradient(90deg, color-mix(in srgb, var(--pi-signal) 8%, transparent) 1px, transparent 1px) 0 0 / 18px 18px,\n var(--pi-paper);\n box-shadow: var(--pi-shadow);\n}\n\nheader {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: 10px;\n border-bottom: 1px solid var(--pi-line);\n background: var(--pi-header);\n padding: 0.8rem 0.9rem 0.7rem;\n}\n\n[data-identity],\n[data-toggle] > span:last-child {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 9px;\n}\n\n[data-identity] > span:last-child {\n display: flex;\n min-width: 0;\n flex-direction: column;\n}\n\n[data-identity] > [data-pi-glyph] {\n display: none;\n}\n\n[data-kicker],\n[data-status],\n[data-scope],\n[data-selection-help],\n[data-activity],\n[data-output-truncated],\n[data-cancel-warning] {\n font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n}\n\n[data-kicker] {\n margin-bottom: 0.15rem;\n color: var(--pi-signal);\n font-size: 0.65rem;\n font-weight: 800;\n letter-spacing: 0.13em;\n}\n\n[data-identity] small,\n[data-scope],\n[data-selection-help],\n[data-activity],\n[data-output-truncated],\n[data-cancel-warning] {\n color: var(--pi-muted);\n font-size: 0.66rem;\n}\n\n[data-identity] small {\n overflow: hidden;\n font-size: 0.68rem;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n[data-pi-glyph] {\n display: inline-grid;\n width: 1.45rem;\n height: 1.45rem;\n flex: 0 0 auto;\n border-radius: 50%;\n text-align: center;\n background: var(--pi-signal);\n color: var(--pi-on-signal);\n font-size: 1rem;\n font-weight: 700;\n line-height: 1.3;\n}\n\n[data-status] {\n display: flex;\n align-items: center;\n gap: 0.4rem;\n color: var(--pi-muted);\n font-size: 0.64rem;\n font-weight: 700;\n line-height: 1;\n white-space: nowrap;\n}\n\n[data-status]::before {\n width: 0.42rem;\n height: 0.42rem;\n flex: 0 0 auto;\n border-radius: 50%;\n background: var(--pi-status-muted);\n content: \"\";\n}\n\n[data-status][data-state=\"ready\"]::before,\n[data-status][data-state=\"completed\"]::before {\n background: var(--pi-success);\n}\n\n[data-status][data-state=\"running\"]::before,\n[data-status][data-state=\"cancelling\"]::before,\n[data-status][data-state=\"busy\"]::before,\n[data-status][data-state=\"resetting\"]::before {\n background: var(--pi-mark);\n box-shadow: 0 0 0 4px color-mix(in srgb, var(--pi-mark) 18%, transparent);\n}\n\n[data-status][data-state=\"failed\"]::before,\n[data-status][data-state=\"unavailable\"]::before {\n background: var(--pi-danger);\n}\n\n[data-focus],\n[data-instruction],\n[data-feedback] {\n padding: 0.75rem 0.9rem;\n}\n\n[data-focus],\n[data-instruction] {\n border-bottom: 1px solid var(--pi-line);\n}\n\nh2,\nlabel {\n display: block;\n margin: 0 0 0.4rem;\n font-size: 0.73rem;\n font-weight: 750;\n}\n\nh2 {\n color: var(--pi-muted);\n font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n font-size: 0.65rem;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n[data-focus-row] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n\n[data-focus-row] p {\n margin: 0;\n}\n\n[data-scope] {\n font-size: 0.68rem;\n}\n\n[data-selection-help] {\n margin: 0.55rem 0 0;\n font-size: 0.66rem;\n}\n\n[data-mark],\n[data-clear] {\n min-height: 34px;\n flex: 0 0 auto;\n padding: 0 0.65rem;\n}\n\n[data-mark] {\n position: relative;\n margin-left: auto;\n border-color: var(--pi-signal);\n background: var(--pi-paper-strong);\n color: var(--pi-signal);\n font-weight: 650;\n}\n\n[data-mark]::before,\n[data-mark]::after {\n position: absolute;\n width: 0.38rem;\n height: 0.38rem;\n border-color: currentcolor;\n content: \"\";\n}\n\n[data-mark]::before {\n top: -0.22rem;\n left: -0.22rem;\n border-top: 2px solid;\n border-left: 2px solid;\n}\n\n[data-mark]::after {\n right: -0.22rem;\n bottom: -0.22rem;\n border-right: 2px solid;\n border-bottom: 2px solid;\n}\n\n[data-mark]:hover,\n[data-mark][aria-pressed=\"true\"] {\n border-color: var(--pi-signal);\n background: var(--pi-signal);\n color: var(--pi-on-signal);\n}\n\n[data-marks] {\n display: flex;\n max-height: 7.5rem;\n flex-wrap: wrap;\n gap: 0.4rem;\n overflow-y: auto;\n margin: 0.55rem 0 0.4rem;\n padding: 0;\n list-style: none;\n}\n\n[data-mark-chip] {\n display: inline-flex;\n min-width: 0;\n max-width: 100%;\n align-items: center;\n border: 1px solid var(--pi-mark-border);\n border-radius: 0.3rem;\n background: var(--pi-mark-bg);\n color: var(--pi-mark-ink);\n font-family: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n font-size: 0.66rem;\n}\n\n[data-mark-chip] > span {\n overflow: hidden;\n padding: 0.3rem 0.4rem;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n[data-mark-chip] button {\n display: grid;\n min-width: 34px;\n min-height: 34px;\n align-self: stretch;\n place-items: center;\n border: 0;\n border-left: 1px solid var(--pi-mark-separator);\n border-radius: 0;\n background: transparent;\n padding: 0 0.4rem;\n color: inherit;\n}\n\n[data-mark-chip] button:hover {\n background: var(--pi-mark-hover);\n}\n\n[data-clear] {\n background: transparent;\n color: var(--pi-muted);\n font-size: 0.72rem;\n}\n\ntextarea {\n display: block;\n width: 100%;\n min-height: 84px;\n border: 1px solid var(--pi-input-border);\n border-radius: 0.375rem;\n background: var(--pi-paper-strong);\n padding: 0.55rem;\n color: var(--pi-ink);\n font: 0.8rem/1.4 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n resize: vertical;\n}\n\ntextarea::placeholder {\n color: var(--pi-muted);\n}\n\ntextarea:focus {\n border-color: var(--pi-signal);\n box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--pi-signal) 18%, transparent);\n}\n\n[data-feedback] {\n display: grid;\n gap: 0.5rem;\n}\n\n[data-feedback] > * {\n margin: 0;\n}\n\n[data-activity],\n[data-error] {\n font-size: 0.72rem;\n}\n\npre {\n max-height: 9rem;\n overflow: auto;\n border: 1px solid var(--pi-line);\n border-radius: 0.3rem;\n background: var(--pi-output-bg);\n padding: 0.55rem;\n color: var(--pi-output-ink);\n font: 0.68rem/1.5 \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n\n[data-error] {\n border-left: 3px solid var(--pi-danger);\n background: var(--pi-error-bg);\n padding: 0.45rem 0.55rem;\n color: var(--pi-error-ink);\n}\n\n[data-insecure-remote-warning] {\n margin: 0.75rem 0.9rem 0;\n border: 1px solid var(--pi-mark-border);\n border-radius: 0.3rem;\n background: var(--pi-mark-bg);\n padding: 0.45rem 0.55rem;\n color: var(--pi-mark-ink);\n font-size: 0.72rem;\n}\n\nfooter {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n border-top: 1px solid var(--pi-line);\n background: var(--pi-footer);\n padding: 0.75rem 0.9rem;\n}\n\nbutton {\n min-height: 34px;\n border: 1px solid var(--pi-button-border);\n border-radius: 0.375rem;\n background: var(--pi-button-bg);\n color: var(--pi-ink);\n cursor: pointer;\n font: 650 0.74rem/1.35 Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n}\n\nbutton:hover {\n background: var(--pi-button-hover);\n}\n\nbutton:focus-visible,\ntextarea:focus-visible,\npre:focus-visible {\n outline: 3px solid var(--pi-signal);\n outline-offset: 2px;\n}\n\nbutton:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n}\n\n[data-close] {\n display: grid;\n width: 34px;\n padding: 0;\n place-items: center;\n border-radius: 50%;\n background: transparent;\n color: var(--pi-muted);\n}\n\n[data-close-icon],\n[data-remove-icon] {\n display: block;\n width: 0.875rem;\n height: 0.875rem;\n fill: none;\n stroke: currentcolor;\n stroke-linecap: round;\n stroke-width: 1.5;\n}\n\n[data-actions] {\n display: flex;\n gap: 0.5rem;\n margin-left: auto;\n}\n\n[data-copy] {\n border-color: var(--pi-signal);\n background: transparent;\n color: var(--pi-signal);\n font-weight: 700;\n padding: 0 0.65rem;\n}\n\n[data-copy]:hover {\n background: color-mix(in srgb, var(--pi-signal) 14%, transparent);\n}\n\n[data-run] {\n min-width: 112px;\n border-color: var(--pi-signal);\n background: var(--pi-signal);\n color: var(--pi-on-signal);\n font-weight: 700;\n}\n\n[data-run]:hover {\n border-color: var(--pi-run-hover);\n background: var(--pi-run-hover);\n}\n\n[data-hover-outline],\n[data-mark-outline] {\n position: fixed;\n z-index: 2147483646;\n pointer-events: none;\n}\n\n[data-hover-outline] {\n border: 2px solid var(--pi-signal);\n box-shadow: 0 0 0 2px var(--pi-overlay-light), 0 0 0 4px var(--pi-overlay-dark);\n}\n\n[data-mark-outline] {\n display: grid;\n place-items: start end;\n border: 2px solid var(--pi-mark);\n color: var(--pi-on-mark);\n font: bold 12px/1 Inter, ui-sans-serif, system-ui, sans-serif;\n box-shadow: 0 0 0 2px var(--pi-overlay-light), 0 0 0 4px var(--pi-overlay-dark);\n}\n\n[data-mark-outline]::after {\n padding: 3px;\n background: var(--pi-mark);\n content: \"marked\";\n}\n\n[data-toggle] {\n position: fixed;\n z-index: 2147483647;\n bottom: 16px;\n left: 16px;\n display: inline-flex;\n min-height: 40px;\n max-width: calc(100% - 32px);\n align-items: center;\n gap: 0.45rem;\n border: 1px solid var(--pi-toggle-border);\n border-radius: 999px;\n background: var(--pi-toggle-bg);\n box-shadow: var(--pi-toggle-shadow);\n padding: 0.38rem 0.72rem 0.38rem 0.4rem;\n color: white;\n text-align: left;\n}\n\n[data-toggle] strong {\n display: inline-flex;\n min-width: 0;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 0.35rem;\n color: white;\n font-size: 0.74rem;\n font-weight: 700;\n letter-spacing: 0.01em;\n}\n\n[data-toggle-scope] {\n color: var(--pi-toggle-muted);\n font-size: 0.68rem;\n font-weight: 600;\n}\n\n[data-toggle]:hover {\n background: var(--pi-toggle-hover);\n}\n\n[data-toggle]:focus-visible {\n outline: 0;\n box-shadow:\n var(--pi-toggle-shadow),\n 0 0 0 2px var(--pi-overlay-light),\n 0 0 0 4px var(--pi-overlay-dark);\n}\n\n@media (max-width: 480px) {\n [data-panel] {\n right: 0.65rem;\n bottom: 0.65rem;\n left: 0.65rem;\n width: auto;\n max-height: calc(100vh - 1.3rem);\n max-height: calc(100dvh - 1.3rem);\n }\n\n [data-focus-row] {\n align-items: stretch;\n flex-direction: column;\n }\n\n [data-mark] {\n align-self: flex-start;\n margin-left: 0;\n }\n\n footer {\n align-items: stretch;\n flex-direction: column;\n }\n\n [data-actions] {\n margin-left: 0;\n }\n\n [data-actions] button {\n min-width: 0;\n flex: 1 1 0;\n }\n\n [data-toggle] {\n bottom: 0.65rem;\n left: 0.65rem;\n }\n}\n\n@media (prefers-reduced-motion: no-preference) {\n [data-toggle] {\n transition: background-color 120ms ease;\n }\n\n [data-status][data-state=\"running\"]::before {\n animation: pi-taskbar-pulse 1.2s ease-in-out infinite;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n scroll-behavior: auto !important;\n animation: none !important;\n transition: none !important;\n }\n}\n\n@keyframes pi-taskbar-pulse {\n 50% {\n box-shadow: 0 0 0 7px color-mix(in srgb, var(--pi-mark) 8%, transparent);\n }\n}";
1181
1291
  const contextProvider = ({
1182
1292
  framework: "rails",
1183
1293
  sourceHint(element) {
@@ -1276,7 +1386,7 @@ function hint(status, references = []) {
1276
1386
  contractVersion: 1,
1277
1387
  contextProvider,
1278
1388
  framework: "rails",
1279
- productVersion: "0.3.0",
1389
+ productVersion: "0.4.1",
1280
1390
  refreshOnCompletion: true,
1281
1391
  });
1282
1392
 
@@ -1284,7 +1394,7 @@ function hint(status, references = []) {
1284
1394
  contractVersion: 1,
1285
1395
  framework: "rails",
1286
1396
  mount: client.mount,
1287
- productVersion: "0.3.0",
1397
+ productVersion: "0.4.1",
1288
1398
  });
1289
1399
 
1290
1400
  const bootstrap = globalThis.document?.querySelector?.("[data-pi-browser-taskbar-bootstrap]");
@@ -4,7 +4,7 @@ module Pi
4
4
  module Browser
5
5
  module Taskbar
6
6
  module Rails
7
- VERSION = "0.3.0"
7
+ VERSION = "0.4.1"
8
8
  end
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pi-browser-taskbar-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Lens
@@ -60,8 +60,8 @@ licenses:
60
60
  - MIT
61
61
  metadata:
62
62
  homepage_uri: https://github.com/davelens/pi-browser-taskbar
63
- source_code_uri: https://github.com/davelens/pi-browser-taskbar/tree/v0.3.0
64
- changelog_uri: https://github.com/davelens/pi-browser-taskbar/blob/v0.3.0/packages/rails/CHANGELOG.md
63
+ source_code_uri: https://github.com/davelens/pi-browser-taskbar/tree/v0.4.1
64
+ changelog_uri: https://github.com/davelens/pi-browser-taskbar/blob/v0.4.1/packages/rails/CHANGELOG.md
65
65
  post_install_message:
66
66
  rdoc_options: []
67
67
  require_paths: