gentle-pi 4.3.0 → 4.5.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.
Files changed (218) hide show
  1. package/README.md +10 -7
  2. package/assets/agents/gentle-ai-worker.md +1 -1
  3. package/assets/design-canvas/canvas.css +648 -0
  4. package/assets/design-canvas/canvas.js +1497 -0
  5. package/assets/design-canvas/index.html +88 -0
  6. package/assets/design-canvas/inspector.js +290 -0
  7. package/assets/orchestrator-delegation-reference.md +2 -2
  8. package/assets/orchestrator-verification.md +1 -1
  9. package/assets/orchestrator.md +2 -2
  10. package/bin/engram-guard +1 -1
  11. package/bin/gentle-shell.mjs +138 -155
  12. package/docs/bundled-install.md +47 -42
  13. package/docs/delegated-verification.md +6 -0
  14. package/docs/gentle-agents-activity.md +5 -1
  15. package/docs/gentle-music.md +82 -0
  16. package/docs/gentle-shell.md +80 -10
  17. package/docs/install-wizard.md +30 -6
  18. package/docs/permission-modes.md +7 -2
  19. package/docs/readme-reference.md +72 -55
  20. package/docs/session-profile-format.md +26 -0
  21. package/docs/voice.md +259 -0
  22. package/docs/yolo-mode.md +17 -2
  23. package/extensions/ask-user-choice.ts +4 -1
  24. package/extensions/gentle-agents.ts +187 -40
  25. package/extensions/gentle-ai.ts +1765 -1185
  26. package/extensions/gentle-design.ts +158 -0
  27. package/extensions/gentle-music.ts +354 -0
  28. package/extensions/gentle-shell.ts +418 -79
  29. package/extensions/gentle-voice.ts +11 -0
  30. package/extensions/session-scratch.ts +41 -22
  31. package/extensions/skill-registry.ts +63 -8
  32. package/lib/agent-profiles.ts +26 -0
  33. package/lib/agents-config.ts +35 -0
  34. package/lib/agents-protocol.ts +57 -1
  35. package/lib/agents-runner.ts +52 -3
  36. package/lib/agents-session-transport.ts +101 -6
  37. package/lib/agents-widget.ts +14 -1
  38. package/lib/auto-permission-memory.ts +48 -18
  39. package/lib/auto-permission-runtime.ts +21 -6
  40. package/lib/clipboard-image.ts +147 -0
  41. package/lib/command-palette-catalog.ts +3 -2
  42. package/lib/commit-slice-signal.ts +1 -1
  43. package/lib/context-backstop.ts +36 -0
  44. package/lib/design-canvas-server.ts +947 -0
  45. package/lib/design-comments.ts +437 -0
  46. package/lib/design-handoffs.ts +313 -0
  47. package/lib/design-open-browser.ts +58 -0
  48. package/lib/design-sources.ts +120 -0
  49. package/lib/external-editor.ts +31 -0
  50. package/lib/gentle-design.ts +513 -0
  51. package/lib/gentle-shell-bundled.ts +141 -57
  52. package/lib/gentle-shell-launcher.ts +11 -4
  53. package/lib/gentle-shell-startup-update.ts +61 -9
  54. package/lib/gentle-shell-uninstall.ts +33 -1
  55. package/lib/image-attachments.ts +150 -0
  56. package/lib/inprocess-reviewer.ts +33 -2
  57. package/lib/jobs-sidebar-state.ts +22 -3
  58. package/lib/jobs-widget.ts +112 -0
  59. package/lib/nan-provider.ts +3 -0
  60. package/lib/native-review-cli.ts +11 -4
  61. package/lib/notification-audio-native.ts +149 -13
  62. package/lib/notification-audio.ts +4 -0
  63. package/lib/notification-pulse-worker.ts +82 -18
  64. package/lib/notification-service.ts +5 -1
  65. package/lib/orchestrator-presence.ts +78 -8
  66. package/lib/orchestrator-scope.ts +10 -0
  67. package/lib/orchestrator-state.ts +11 -3
  68. package/lib/permission-mode-policy.ts +2 -2
  69. package/lib/review-host-relay.ts +11 -2
  70. package/lib/review-reminder-receipt.ts +24 -1
  71. package/lib/session-profile-append-controller.ts +713 -0
  72. package/lib/session-profile-disk-reader.ts +182 -0
  73. package/lib/session-profile-freeze.ts +2 -2
  74. package/lib/session-profile-persistence.ts +15 -7
  75. package/lib/session-scratch.ts +52 -3
  76. package/lib/session-worktree-registry.ts +7 -0
  77. package/lib/shell-bar.ts +6 -7
  78. package/lib/shell-changes.ts +81 -5
  79. package/lib/shell-prompt.ts +4 -4
  80. package/lib/shell-sidebar-layout.ts +27 -7
  81. package/lib/shell-todo.ts +1 -1
  82. package/lib/verifier-admission.ts +5 -4
  83. package/lib/vim-editor-adapter.ts +14 -10
  84. package/lib/voice-artifacts.ts +188 -0
  85. package/lib/voice-command.ts +576 -0
  86. package/lib/voice-install.ts +455 -0
  87. package/lib/voice-platform.ts +99 -0
  88. package/lib/voice-push-to-talk.ts +262 -0
  89. package/lib/voice-session.ts +460 -0
  90. package/lib/voice-settings.ts +50 -0
  91. package/lib/voice-ui.ts +158 -0
  92. package/lib/yolo-session-policy.ts +58 -22
  93. package/package.json +1 -1
  94. package/runtime/gentle-shell-bundled.mjs +140 -56
  95. package/runtime/gentle-shell-launcher.mjs +11 -4
  96. package/runtime/gentle-shell-startup-update.mjs +61 -9
  97. package/runtime/gentle-shell-uninstall.mjs +33 -1
  98. package/runtime/native-review-cli.mjs +11 -4
  99. package/scripts/bundled-engram-migration.mjs +0 -6
  100. package/scripts/bundled-install.mjs +209 -13
  101. package/scripts/gentle-ai-installer.mjs +10 -10
  102. package/scripts/installer-downloads.mjs +31 -7
  103. package/scripts/installer-preflight.mjs +6 -4
  104. package/scripts/installer-windows.mjs +37 -7
  105. package/scripts/main-channel.mjs +91 -17
  106. package/scripts/pi-update.mjs +37 -7
  107. package/scripts/verify-package-files.mjs +26 -2
  108. package/scripts/voice-worker.cjs +476 -0
  109. package/skills/design/SKILL.md +110 -0
  110. package/tests/agent-profiles.test.ts +24 -0
  111. package/tests/agents-config.test.ts +61 -0
  112. package/tests/agents-fake-child.ts +8 -1
  113. package/tests/agents-protocol.test.ts +32 -0
  114. package/tests/agents-runner.test.ts +226 -5
  115. package/tests/agents-session-transport.test.ts +53 -3
  116. package/tests/ask-user-choice.test.ts +30 -0
  117. package/tests/auto-mode.test.ts +17 -0
  118. package/tests/auto-permission-memory-diagnostics.test.ts +56 -0
  119. package/tests/auto-permission-runtime.test.ts +85 -1
  120. package/tests/autonomous-guard.test.ts +20 -0
  121. package/tests/bundled-install.test.ts +276 -5
  122. package/tests/clipboard-image.test.ts +114 -0
  123. package/tests/command-palette.test.ts +4 -4
  124. package/tests/context-backstop.test.ts +70 -4
  125. package/tests/delegate-for-reason-contract.test.ts +2 -1
  126. package/tests/design-canvas-assets.test.ts +710 -0
  127. package/tests/design-canvas-server.test.ts +1096 -0
  128. package/tests/design-comments.test.ts +201 -0
  129. package/tests/design-handoffs.test.ts +282 -0
  130. package/tests/design-open-browser.test.ts +70 -0
  131. package/tests/design-sources.test.ts +132 -0
  132. package/tests/engram-safety.test.ts +132 -57
  133. package/tests/external-editor.test.ts +84 -0
  134. package/tests/fixtures/primary-session-prompt/gentleman.golden.md +3 -3
  135. package/tests/gentle-agents.test.ts +369 -24
  136. package/tests/gentle-ai-binary.test.ts +1 -1
  137. package/tests/gentle-ai-installer.test.ts +51 -51
  138. package/tests/gentle-ai.test.ts +928 -448
  139. package/tests/gentle-design.test.ts +1313 -0
  140. package/tests/gentle-music.test.ts +452 -0
  141. package/tests/gentle-shell-bundled.test.ts +321 -248
  142. package/tests/gentle-shell-launcher.test.ts +29 -2
  143. package/tests/gentle-shell-startup-update.test.ts +77 -1
  144. package/tests/gentle-shell-uninstall.test.ts +59 -0
  145. package/tests/gentle-shell.test.ts +227 -27
  146. package/tests/gentle-todo.test.ts +11 -0
  147. package/tests/image-attachments.test.ts +119 -0
  148. package/tests/image-paste-chips.test.ts +236 -0
  149. package/tests/inprocess-reviewer.test.ts +55 -2
  150. package/tests/installer-bundles.test.ts +12 -6
  151. package/tests/installer-downloads.test.ts +32 -0
  152. package/tests/installer-posix-bootstrap.test.ts +8 -8
  153. package/tests/installer-preflight.test.ts +16 -0
  154. package/tests/installer-server.test.ts +2 -2
  155. package/tests/installer-windows-bootstrap.test.ts +61 -3
  156. package/tests/installer-windows-diagnostics.test.ts +111 -0
  157. package/tests/jobs-widget.test.ts +138 -0
  158. package/tests/main-channel-upgrade.test.ts +232 -2
  159. package/tests/main-channel.test.ts +140 -21
  160. package/tests/model-profiles-preservation.test.ts +2 -2
  161. package/tests/nan-provider.test.ts +36 -0
  162. package/tests/native-review-capability-contract.test.ts +11 -4
  163. package/tests/native-review-parity.test.ts +1 -1
  164. package/tests/notification-audio-native.test.ts +128 -9
  165. package/tests/notification-customize.test.ts +17 -5
  166. package/tests/odd-routing-canonical-ratchet.test.ts +2 -1
  167. package/tests/orchestrator-budget.test.ts +5 -2
  168. package/tests/orchestrator-presence.test.ts +100 -2
  169. package/tests/orchestrator-rdd-ownership.test.ts +2 -2
  170. package/tests/orchestrator-scope.test.ts +14 -0
  171. package/tests/orchestrator-state.test.ts +29 -0
  172. package/tests/package-files.test.ts +34 -0
  173. package/tests/package-manifest.test.ts +6 -6
  174. package/tests/permission-mode-policy.test.ts +10 -1
  175. package/tests/pi-update.test.ts +104 -4
  176. package/tests/rdd-aware-verification-contract.test.ts +1 -1
  177. package/tests/review-host-relay-routing.test.ts +88 -1
  178. package/tests/review-host-relay.test.ts +9 -2
  179. package/tests/review-json-arguments.test.ts +20 -3
  180. package/tests/review-loadout-rdd-off-sdk.test.ts +23 -2
  181. package/tests/review-relay-transport-agent.test.ts +26 -0
  182. package/tests/review-reminder-receipt.test.ts +31 -1
  183. package/tests/runtime-harness.mjs +188 -211
  184. package/tests/session-changes.test.ts +2 -1
  185. package/tests/session-profile-append-controller-fork.test.ts +458 -0
  186. package/tests/session-profile-append-controller-pi.test.ts +290 -0
  187. package/tests/session-profile-append-controller.test.ts +731 -0
  188. package/tests/session-profile-disk-reader-pi.test.ts +183 -0
  189. package/tests/session-profile-disk-reader.test.ts +311 -0
  190. package/tests/session-profile-freeze.test.ts +6 -4
  191. package/tests/session-scratch.test.ts +68 -2
  192. package/tests/session-worktree-registry.test.ts +13 -1
  193. package/tests/shell-bar.test.ts +18 -6
  194. package/tests/shell-changes.test.ts +148 -1
  195. package/tests/shell-prompt.test.ts +3 -3
  196. package/tests/shell-render-cache.test.ts +134 -1
  197. package/tests/shell-sidebar-layout.test.ts +67 -0
  198. package/tests/short-check-routing-contract.test.ts +1 -1
  199. package/tests/skill-collision-prefixes.test.ts +3 -1
  200. package/tests/skill-registry-watcher.test.ts +17 -5
  201. package/tests/skill-registry.test.ts +47 -3
  202. package/tests/task-size-routing-contract.test.ts +1 -1
  203. package/tests/test-strength-contract.test.ts +1 -1
  204. package/tests/verifier-admission.test.ts +18 -1
  205. package/tests/vim-editor-adapter.test.ts +2 -2
  206. package/tests/voice-command.test.ts +773 -0
  207. package/tests/voice-install.test.ts +323 -0
  208. package/tests/voice-platform.test.ts +176 -0
  209. package/tests/voice-push-to-talk.test.ts +444 -0
  210. package/tests/voice-session.test.ts +434 -0
  211. package/tests/voice-settings.test.ts +49 -0
  212. package/tests/voice-ui.test.ts +97 -0
  213. package/tests/voice-worker.test.ts +445 -0
  214. package/tests/weak-model-economy-prompt.test.ts +32 -0
  215. package/tests/windows-hidden-processes.test.ts +25 -2
  216. package/tests/yolo-customize.test.ts +5 -1
  217. package/tests/yolo-mode.test.ts +1 -1
  218. package/tests/yolo-session-policy-capture.test.ts +104 -0
@@ -0,0 +1,648 @@
1
+ :root {
2
+ color-scheme: dark;
3
+ --gd-bg: #111214;
4
+ --gd-stage: #1a1b1e;
5
+ --gd-panel: #222327;
6
+ --gd-border: #3a3b40;
7
+ --gd-text: #ececee;
8
+ --gd-muted: #a9abb2;
9
+ --gd-accent: #8ab4ff;
10
+ --gd-accent-text: #0b1220;
11
+ --gd-danger: #ff9b9b;
12
+ --gd-focus: #ffd479;
13
+ --gd-pin: #8ab4ff;
14
+ --gd-pin-resolved: #7d8a7f;
15
+ --gd-success: #9be0a8;
16
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
17
+ font-size: 14px;
18
+ }
19
+
20
+ * { box-sizing: border-box; }
21
+
22
+ html, body {
23
+ margin: 0;
24
+ height: 100%;
25
+ background: var(--gd-bg);
26
+ color: var(--gd-text);
27
+ overflow: hidden;
28
+ }
29
+
30
+ body {
31
+ display: flex;
32
+ flex-direction: column;
33
+ }
34
+
35
+ .gd-visually-hidden {
36
+ position: absolute;
37
+ width: 1px;
38
+ height: 1px;
39
+ overflow: hidden;
40
+ clip: rect(0 0 0 0);
41
+ white-space: nowrap;
42
+ }
43
+
44
+ .gd-bar {
45
+ display: flex;
46
+ align-items: center;
47
+ flex-wrap: wrap;
48
+ gap: 8px 12px;
49
+ padding: 8px 12px;
50
+ background: var(--gd-panel);
51
+ border-bottom: 1px solid var(--gd-border);
52
+ flex: none;
53
+ }
54
+
55
+ .gd-title {
56
+ margin: 0;
57
+ font-size: 15px;
58
+ font-weight: 600;
59
+ white-space: nowrap;
60
+ overflow: hidden;
61
+ text-overflow: ellipsis;
62
+ max-width: 32ch;
63
+ }
64
+
65
+ .gd-group {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: 4px;
69
+ }
70
+
71
+ button, select, textarea {
72
+ font: inherit;
73
+ color: var(--gd-text);
74
+ background: var(--gd-stage);
75
+ border: 1px solid var(--gd-border);
76
+ border-radius: 6px;
77
+ }
78
+
79
+ button {
80
+ min-width: 32px;
81
+ min-height: 32px;
82
+ padding: 4px 10px;
83
+ cursor: pointer;
84
+ white-space: nowrap;
85
+ }
86
+
87
+ button:hover { border-color: var(--gd-muted); }
88
+
89
+ select {
90
+ min-height: 32px;
91
+ padding: 4px 8px;
92
+ max-width: 28ch;
93
+ }
94
+
95
+ button:focus-visible, select:focus-visible, textarea:focus-visible, .gd-viewport:focus-visible {
96
+ outline: 2px solid var(--gd-focus);
97
+ outline-offset: 2px;
98
+ }
99
+
100
+ #gd-zoom-level {
101
+ min-width: 5ch;
102
+ text-align: center;
103
+ font-variant-numeric: tabular-nums;
104
+ color: var(--gd-muted);
105
+ }
106
+
107
+ .gd-toggle[aria-pressed="true"], .gd-primary {
108
+ background: var(--gd-accent);
109
+ border-color: var(--gd-accent);
110
+ color: var(--gd-accent-text);
111
+ }
112
+
113
+ .gd-feature {
114
+ margin: 0;
115
+ max-width: 28ch;
116
+ color: var(--gd-muted);
117
+ white-space: nowrap;
118
+ overflow: hidden;
119
+ text-overflow: ellipsis;
120
+ }
121
+
122
+ .gd-nav-mode {
123
+ margin: 0;
124
+ max-width: 40ch;
125
+ color: var(--gd-muted);
126
+ white-space: nowrap;
127
+ overflow: hidden;
128
+ text-overflow: ellipsis;
129
+ }
130
+
131
+ .gd-nav-mode.gd-using { color: var(--gd-accent); font-weight: 600; }
132
+
133
+ .gd-status {
134
+ margin: 0 0 0 auto;
135
+ color: var(--gd-muted);
136
+ white-space: nowrap;
137
+ overflow: hidden;
138
+ text-overflow: ellipsis;
139
+ }
140
+
141
+ .gd-main {
142
+ position: relative;
143
+ display: flex;
144
+ flex: 1;
145
+ min-height: 0;
146
+ }
147
+
148
+ .gd-viewport {
149
+ position: relative;
150
+ flex: 1;
151
+ min-width: 0;
152
+ overflow: hidden;
153
+ background: var(--gd-stage);
154
+ cursor: grab;
155
+ touch-action: none;
156
+ }
157
+
158
+ .gd-viewport.gd-panning { cursor: grabbing; }
159
+
160
+ .gd-world {
161
+ position: absolute;
162
+ top: 0;
163
+ left: 0;
164
+ display: flex;
165
+ align-items: flex-start;
166
+ gap: 80px;
167
+ padding: 0;
168
+ transform-origin: 0 0;
169
+ will-change: transform;
170
+ }
171
+
172
+ .gd-artboard {
173
+ display: flex;
174
+ flex-direction: column;
175
+ gap: 8px;
176
+ flex: none;
177
+ }
178
+
179
+ .gd-artboard-label {
180
+ position: relative;
181
+ display: flex;
182
+ flex-direction: column;
183
+ gap: 2px;
184
+ }
185
+
186
+ /* The handoff row above Implement this: anchored to the label's bottom right, kept at screen
187
+ size like the pins, and wrapped within the artboard's on-screen width. */
188
+ .gd-board-actions {
189
+ position: absolute;
190
+ right: 0;
191
+ bottom: 0;
192
+ display: flex;
193
+ flex-direction: column;
194
+ align-items: flex-end;
195
+ gap: 4px;
196
+ max-width: calc(100% / var(--gd-inv-zoom, 1));
197
+ transform: scale(var(--gd-inv-zoom, 1));
198
+ transform-origin: right bottom;
199
+ }
200
+
201
+ .gd-implement { white-space: nowrap; }
202
+
203
+ /* At 100% the label makes room for the handoff row above Implement this. */
204
+ .gd-has-handoff .gd-artboard-label { padding-top: 30px; }
205
+
206
+ .gd-implement-confirm {
207
+ display: flex;
208
+ align-items: center;
209
+ gap: 6px;
210
+ padding: 4px 6px;
211
+ border: 1px solid var(--gd-border);
212
+ border-radius: 6px;
213
+ background: var(--gd-panel);
214
+ }
215
+
216
+ .gd-implement [hidden] { display: none; }
217
+
218
+ .gd-artboard-title {
219
+ margin: 0;
220
+ font-size: 16px;
221
+ font-weight: 600;
222
+ }
223
+
224
+ .gd-artboard-tradeoff {
225
+ margin: 0;
226
+ color: var(--gd-muted);
227
+ }
228
+
229
+ /* Handoff row (S15): Chosen or Alternative, the handoff state, its feature document, note and Compare. */
230
+ .gd-handoff {
231
+ display: flex;
232
+ flex-wrap: wrap;
233
+ align-items: center;
234
+ justify-content: flex-end;
235
+ gap: 6px;
236
+ font-size: 12px;
237
+ }
238
+
239
+ .gd-handoff[hidden] { display: none; }
240
+
241
+ .gd-choice, .gd-handoff-badge, .gd-handoff-feature, .gd-handoff-note {
242
+ padding: 1px 8px;
243
+ border: 1px solid var(--gd-border);
244
+ border-radius: 999px;
245
+ background: var(--gd-panel);
246
+ white-space: nowrap;
247
+ }
248
+
249
+ .gd-choice-chosen { background: var(--gd-accent); border-color: var(--gd-accent); color: var(--gd-accent-text); font-weight: 700; }
250
+ .gd-choice-alternative { color: var(--gd-muted); }
251
+ .gd-handoff-badge { font-weight: 600; }
252
+ .gd-handoff-badge[data-status="queued"] { color: var(--gd-focus); border-color: var(--gd-focus); }
253
+ .gd-handoff-badge[data-status="sent"], .gd-handoff-badge[data-status="in_progress"] { color: var(--gd-accent); border-color: var(--gd-accent); }
254
+ .gd-handoff-badge[data-status="implemented"] { color: var(--gd-success); border-color: var(--gd-success); }
255
+ .gd-handoff-feature { color: var(--gd-muted); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
256
+ .gd-handoff-note { max-width: 32ch; overflow: hidden; text-overflow: ellipsis; color: var(--gd-muted); font-style: italic; }
257
+ .gd-compare-start { min-height: 24px; padding: 0 10px; font-size: 12px; }
258
+
259
+ /* The chosen artboard keeps an accent frame at every zoom level. */
260
+ .gd-chosen .gd-artboard-frame { box-shadow: 0 0 0 calc(3px * var(--gd-inv-zoom, 1)) var(--gd-accent), 0 2px 16px rgba(0, 0, 0, 0.5); }
261
+
262
+ .gd-artboard-frame {
263
+ display: block;
264
+ border: 0;
265
+ background: #fff;
266
+ box-shadow: 0 2px 16px rgba(0, 0, 0, 0.5);
267
+ }
268
+
269
+ .gd-commenting .gd-artboard-frame { outline: 2px dashed var(--gd-accent); outline-offset: 4px; }
270
+
271
+ /* The system board never takes comments: no comment outline, and a note while Comment mode is on. */
272
+ .gd-commenting .gd-system-board .gd-artboard-frame { outline: none; opacity: 0.6; }
273
+ .gd-system-note { visibility: hidden; margin: 0; color: var(--gd-muted); font-style: italic; cursor: help; }
274
+ .gd-commenting .gd-system-note { visibility: visible; }
275
+
276
+ .gd-frame-box { position: relative; }
277
+
278
+ /* Navigation mode: a shield over each artboard keeps the wheel, drags and clicks on the canvas.
279
+ Focus mode removes it from the focused artboard only. */
280
+ .gd-shield {
281
+ position: absolute;
282
+ inset: 0;
283
+ cursor: pointer;
284
+ outline: none;
285
+ }
286
+
287
+ .gd-shield[hidden] { display: none; }
288
+
289
+ .gd-shield:hover, .gd-shield:focus-visible {
290
+ background: rgba(138, 180, 255, 0.06);
291
+ box-shadow: inset 0 0 0 calc(2px * var(--gd-inv-zoom, 1)) var(--gd-accent);
292
+ }
293
+
294
+ .gd-shield:focus-visible { box-shadow: inset 0 0 0 calc(3px * var(--gd-inv-zoom, 1)) var(--gd-focus); }
295
+
296
+ .gd-shield-hint {
297
+ position: absolute;
298
+ top: 0;
299
+ left: 0;
300
+ right: 0;
301
+ width: max-content;
302
+ margin: 12px auto 0;
303
+ padding: 4px 10px;
304
+ border: 1px solid var(--gd-border);
305
+ border-radius: 999px;
306
+ background: var(--gd-panel);
307
+ color: var(--gd-text);
308
+ white-space: nowrap;
309
+ pointer-events: none;
310
+ opacity: 0;
311
+ transform: scale(var(--gd-inv-zoom, 1));
312
+ transform-origin: center top;
313
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
314
+ }
315
+
316
+ .gd-shield:hover .gd-shield-hint, .gd-shield:focus-visible .gd-shield-hint { opacity: 1; }
317
+
318
+ /* In comment mode the inspector draws the hover box itself. */
319
+ .gd-commenting .gd-shield { cursor: crosshair; }
320
+ .gd-commenting .gd-shield:hover { background: transparent; box-shadow: none; }
321
+ .gd-commenting .gd-shield-hint { display: none; }
322
+
323
+ .gd-viewport.gd-focused { cursor: default; }
324
+ .gd-focused .gd-artboard:not(.gd-focus-board) { opacity: 0.25; }
325
+ .gd-focused .gd-focus-board .gd-artboard-frame { box-shadow: 0 0 0 2px var(--gd-accent), 0 2px 16px rgba(0, 0, 0, 0.5); }
326
+
327
+ /* Floats in the band canvas.js keeps free below the focused artboard. */
328
+ .gd-back {
329
+ position: absolute;
330
+ bottom: 16px;
331
+ left: 50%;
332
+ transform: translateX(-50%);
333
+ z-index: 5;
334
+ background: var(--gd-accent);
335
+ border-color: var(--gd-accent);
336
+ color: var(--gd-accent-text);
337
+ font-weight: 600;
338
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.6);
339
+ }
340
+
341
+ .gd-back[hidden] { display: none; }
342
+
343
+ /* Pins live in the transformed world, so they follow pan and zoom; the inverse scale keeps their size. */
344
+ .gd-pins {
345
+ position: absolute;
346
+ inset: 0;
347
+ pointer-events: none;
348
+ }
349
+
350
+ .gd-pin {
351
+ position: absolute;
352
+ min-width: 26px;
353
+ min-height: 26px;
354
+ padding: 0 6px;
355
+ border-radius: 13px 13px 13px 2px;
356
+ border: 2px solid #0b1220;
357
+ background: var(--gd-pin);
358
+ color: var(--gd-accent-text);
359
+ font-weight: 700;
360
+ font-size: 12px;
361
+ pointer-events: auto;
362
+ transform: translate(-2px, -100%) scale(var(--gd-inv-zoom, 1));
363
+ transform-origin: 0 100%;
364
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
365
+ }
366
+
367
+ .gd-pin[data-status="resolved"] { background: var(--gd-pin-resolved); opacity: 0.75; }
368
+ .gd-pin.gd-pin-active { background: var(--gd-focus); opacity: 1; }
369
+
370
+ .gd-radio {
371
+ display: inline-flex;
372
+ align-items: center;
373
+ gap: 4px;
374
+ color: var(--gd-muted);
375
+ white-space: nowrap;
376
+ cursor: pointer;
377
+ }
378
+
379
+ .gd-radio input { accent-color: var(--gd-accent); margin: 0; }
380
+ .gd-radio input:focus-visible { outline: 2px solid var(--gd-focus); outline-offset: 2px; }
381
+
382
+ button:disabled { opacity: 0.5; cursor: default; }
383
+
384
+ .gd-panel {
385
+ flex: none;
386
+ width: min(340px, 40vw);
387
+ overflow-y: auto;
388
+ padding: 12px;
389
+ background: var(--gd-panel);
390
+ border-left: 1px solid var(--gd-border);
391
+ }
392
+
393
+ .gd-panel[hidden] { display: none; }
394
+
395
+ .gd-panel-title {
396
+ margin: 0 0 8px;
397
+ font-size: 15px;
398
+ }
399
+
400
+ .gd-filter { margin-bottom: 8px; }
401
+ .gd-filter button[aria-pressed="true"] { border-color: var(--gd-accent); color: var(--gd-accent); }
402
+
403
+ .gd-panel-empty { color: var(--gd-muted); margin: 8px 0; }
404
+
405
+ .gd-comment-list {
406
+ list-style: none;
407
+ margin: 0;
408
+ padding: 0;
409
+ display: flex;
410
+ flex-direction: column;
411
+ gap: 8px;
412
+ }
413
+
414
+ .gd-comment-item {
415
+ border: 1px solid var(--gd-border);
416
+ border-radius: 6px;
417
+ background: var(--gd-stage);
418
+ }
419
+
420
+ .gd-comment-item.gd-active { border-color: var(--gd-focus); }
421
+
422
+ .gd-comment-show {
423
+ display: block;
424
+ white-space: normal;
425
+ width: 100%;
426
+ text-align: left;
427
+ border: 0;
428
+ background: transparent;
429
+ padding: 8px;
430
+ }
431
+
432
+ .gd-comment-meta {
433
+ display: flex;
434
+ flex-wrap: wrap;
435
+ gap: 6px;
436
+ color: var(--gd-muted);
437
+ font-size: 12px;
438
+ }
439
+
440
+ .gd-comment-number { color: var(--gd-text); font-weight: 700; }
441
+ .gd-comment-text { margin: 4px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }
442
+ .gd-badge { border: 1px solid var(--gd-border); border-radius: 4px; padding: 0 4px; }
443
+ .gd-badge-detached { color: var(--gd-danger); border-color: var(--gd-danger); }
444
+ .gd-badge-sent { color: var(--gd-success); border-color: var(--gd-success); }
445
+
446
+ .gd-comment-actions {
447
+ display: flex;
448
+ justify-content: flex-end;
449
+ padding: 0 8px 8px;
450
+ }
451
+
452
+ .gd-empty {
453
+ position: absolute;
454
+ inset: 0;
455
+ margin: auto;
456
+ height: fit-content;
457
+ max-width: 48ch;
458
+ text-align: center;
459
+ color: var(--gd-muted);
460
+ }
461
+
462
+ .gd-popover {
463
+ position: fixed;
464
+ z-index: 10;
465
+ width: min(360px, calc(100vw - 24px));
466
+ padding: 12px;
467
+ background: var(--gd-panel);
468
+ border: 1px solid var(--gd-border);
469
+ border-radius: 8px;
470
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
471
+ }
472
+
473
+ .gd-popover-title {
474
+ margin: 0 0 8px;
475
+ color: var(--gd-muted);
476
+ overflow: hidden;
477
+ text-overflow: ellipsis;
478
+ white-space: nowrap;
479
+ }
480
+
481
+ .gd-popover-title code { color: var(--gd-text); }
482
+
483
+ .gd-popover textarea {
484
+ width: 100%;
485
+ resize: vertical;
486
+ padding: 8px;
487
+ }
488
+
489
+ .gd-popover.gd-dropping { border-color: var(--gd-accent); }
490
+
491
+ .gd-shot {
492
+ display: flex;
493
+ align-items: flex-start;
494
+ gap: 8px;
495
+ margin-top: 8px;
496
+ }
497
+
498
+ .gd-shot-preview {
499
+ display: flex;
500
+ align-items: flex-start;
501
+ gap: 8px;
502
+ margin: 0;
503
+ }
504
+
505
+ .gd-shot-preview[hidden] { display: none; }
506
+
507
+ .gd-shot-preview img, .gd-comment-shot {
508
+ display: block;
509
+ max-width: 160px;
510
+ max-height: 96px;
511
+ object-fit: contain;
512
+ border: 1px solid var(--gd-border);
513
+ border-radius: 4px;
514
+ background: var(--gd-bg);
515
+ }
516
+
517
+ .gd-comment-shot { margin-top: 6px; }
518
+
519
+ .gd-popover-actions {
520
+ display: flex;
521
+ align-items: center;
522
+ justify-content: flex-end;
523
+ gap: 8px;
524
+ margin-top: 8px;
525
+ }
526
+
527
+ .gd-popover-message {
528
+ margin-right: auto;
529
+ color: var(--gd-muted);
530
+ }
531
+
532
+ .gd-popover-message.gd-error, .gd-status.gd-error { color: var(--gd-danger); }
533
+
534
+ /* Compare (S15): the artboard and the running app side by side, over the canvas and the panel. */
535
+ .gd-compare {
536
+ position: absolute;
537
+ inset: 0;
538
+ z-index: 8;
539
+ display: flex;
540
+ flex-direction: column;
541
+ background: var(--gd-stage);
542
+ }
543
+
544
+ .gd-compare[hidden] { display: none; }
545
+
546
+ .gd-compare-bar {
547
+ display: flex;
548
+ align-items: center;
549
+ flex-wrap: wrap;
550
+ gap: 8px 12px;
551
+ padding: 8px 12px;
552
+ background: var(--gd-panel);
553
+ border-bottom: 1px solid var(--gd-border);
554
+ }
555
+
556
+ .gd-compare-title {
557
+ margin: 0 auto 0 0;
558
+ font-size: 15px;
559
+ white-space: nowrap;
560
+ overflow: hidden;
561
+ text-overflow: ellipsis;
562
+ }
563
+
564
+ .gd-compare-bar .gd-radio[hidden] { display: none; }
565
+
566
+ .gd-compare-open {
567
+ display: inline-flex;
568
+ align-items: center;
569
+ min-height: 32px;
570
+ padding: 4px 10px;
571
+ border: 1px solid var(--gd-border);
572
+ border-radius: 6px;
573
+ color: var(--gd-accent);
574
+ }
575
+
576
+ .gd-compare-open:focus-visible { outline: 2px solid var(--gd-focus); outline-offset: 2px; }
577
+
578
+ .gd-compare-message {
579
+ margin: 0;
580
+ padding: 6px 12px;
581
+ color: var(--gd-muted);
582
+ }
583
+
584
+ .gd-compare-message.gd-error { color: var(--gd-danger); }
585
+
586
+ .gd-compare-stage {
587
+ flex: 1;
588
+ min-height: 0;
589
+ overflow: auto;
590
+ padding: 24px;
591
+ }
592
+
593
+ .gd-compare-sizer { overflow: hidden; }
594
+
595
+ .gd-compare-panes {
596
+ display: flex;
597
+ align-items: flex-start;
598
+ gap: 24px;
599
+ width: max-content;
600
+ transform-origin: 0 0;
601
+ }
602
+
603
+ .gd-compare-pane {
604
+ display: flex;
605
+ flex-direction: column;
606
+ gap: 6px;
607
+ margin: 0;
608
+ }
609
+
610
+ .gd-compare-caption {
611
+ max-width: 100%;
612
+ color: var(--gd-muted);
613
+ white-space: nowrap;
614
+ overflow: hidden;
615
+ text-overflow: ellipsis;
616
+ }
617
+
618
+ .gd-compare-frame {
619
+ display: block;
620
+ border: 0;
621
+ background: #fff;
622
+ box-shadow: 0 2px 16px rgba(0, 0, 0, 0.5);
623
+ }
624
+
625
+ /* A confirmation that outlives the status line, such as a sent handoff. */
626
+ .gd-toast {
627
+ position: fixed;
628
+ left: 50%;
629
+ bottom: 24px;
630
+ z-index: 20;
631
+ max-width: min(560px, calc(100vw - 32px));
632
+ padding: 10px 16px;
633
+ transform: translateX(-50%);
634
+ background: var(--gd-panel);
635
+ border: 1px solid var(--gd-accent);
636
+ border-radius: 8px;
637
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
638
+ font-weight: 600;
639
+ }
640
+
641
+ .gd-toast[hidden] { display: none; }
642
+ .gd-toast.gd-error { border-color: var(--gd-danger); color: var(--gd-danger); }
643
+
644
+ @media (prefers-reduced-motion: no-preference) {
645
+ button { transition: border-color 120ms ease; }
646
+ .gd-shield-hint { transition: opacity 120ms ease; }
647
+ .gd-artboard { transition: opacity 160ms ease; }
648
+ }