@aiworker/sdk 1.24.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 (77) hide show
  1. package/API.md +575 -0
  2. package/DOCUMENTATION.md +120 -0
  3. package/GUEST_SERVICES.md +166 -0
  4. package/LICENSE +21 -0
  5. package/README.md +249 -0
  6. package/dist/api-version.d.ts +6 -0
  7. package/dist/api-version.js +5 -0
  8. package/dist/contract.d.ts +504 -0
  9. package/dist/contract.js +256 -0
  10. package/dist/host-version.d.ts +16 -0
  11. package/dist/host-version.js +50 -0
  12. package/dist/host.d.ts +107 -0
  13. package/dist/host.js +639 -0
  14. package/dist/index.d.ts +13 -0
  15. package/dist/index.js +8 -0
  16. package/dist/manifest.d.ts +303 -0
  17. package/dist/manifest.js +222 -0
  18. package/dist/parse.d.ts +293 -0
  19. package/dist/parse.js +398 -0
  20. package/dist/protocol.d.ts +1148 -0
  21. package/dist/protocol.js +473 -0
  22. package/dist/schemas.d.ts +4 -0
  23. package/dist/schemas.js +6 -0
  24. package/dist/scrollbar-style.d.ts +2 -0
  25. package/dist/scrollbar-style.js +32 -0
  26. package/dist/ui/badge.d.ts +10 -0
  27. package/dist/ui/badge.js +26 -0
  28. package/dist/ui/banner.d.ts +13 -0
  29. package/dist/ui/banner.js +48 -0
  30. package/dist/ui/button.d.ts +14 -0
  31. package/dist/ui/button.js +49 -0
  32. package/dist/ui/checkbox.d.ts +12 -0
  33. package/dist/ui/checkbox.js +45 -0
  34. package/dist/ui/dom.d.ts +15 -0
  35. package/dist/ui/dom.js +60 -0
  36. package/dist/ui/empty.d.ts +11 -0
  37. package/dist/ui/empty.js +44 -0
  38. package/dist/ui/field.d.ts +18 -0
  39. package/dist/ui/field.js +49 -0
  40. package/dist/ui/icons.d.ts +10 -0
  41. package/dist/ui/icons.js +23 -0
  42. package/dist/ui/index.d.ts +35 -0
  43. package/dist/ui/index.js +17 -0
  44. package/dist/ui/list.d.ts +26 -0
  45. package/dist/ui/list.js +90 -0
  46. package/dist/ui/menu.d.ts +20 -0
  47. package/dist/ui/menu.js +115 -0
  48. package/dist/ui/navigation.d.ts +18 -0
  49. package/dist/ui/navigation.js +38 -0
  50. package/dist/ui/option.d.ts +16 -0
  51. package/dist/ui/option.js +35 -0
  52. package/dist/ui/popup.d.ts +5 -0
  53. package/dist/ui/popup.js +43 -0
  54. package/dist/ui/progress.d.ts +11 -0
  55. package/dist/ui/progress.js +44 -0
  56. package/dist/ui/search.d.ts +11 -0
  57. package/dist/ui/search.js +62 -0
  58. package/dist/ui/select.d.ts +22 -0
  59. package/dist/ui/select.js +168 -0
  60. package/dist/ui/separator.d.ts +6 -0
  61. package/dist/ui/separator.js +26 -0
  62. package/dist/ui/spinner.d.ts +8 -0
  63. package/dist/ui/spinner.js +29 -0
  64. package/dist/ui/style.d.ts +1 -0
  65. package/dist/ui/style.js +202 -0
  66. package/dist/ui/tabs.d.ts +15 -0
  67. package/dist/ui/tabs.js +61 -0
  68. package/dist/ui/text.d.ts +23 -0
  69. package/dist/ui/text.js +89 -0
  70. package/dist/ui/theme.d.ts +22 -0
  71. package/dist/ui/theme.js +79 -0
  72. package/dist/workspace-schemas.d.ts +136 -0
  73. package/dist/workspace-schemas.js +44 -0
  74. package/dist/workspace.d.ts +109 -0
  75. package/dist/workspace.js +4 -0
  76. package/package.json +55 -0
  77. package/scripts/bundle-guest.ts +44 -0
@@ -0,0 +1,202 @@
1
+ import { GUEST_SCROLLBAR_CSS } from "../scrollbar-style.js";
2
+ /** Host CSS variable → `--oc-*` alias written by `theme.ts`. */
3
+ const OC_ALIAS = {
4
+ 'surface-background': 'bg',
5
+ 'surface-elevated': 'elevated',
6
+ 'surface-elevated-foreground': 'elevated-fg',
7
+ 'surface-foreground': 'fg',
8
+ 'surface-muted-foreground': 'muted',
9
+ 'surface-muted': 'muted-surface',
10
+ 'surface-subtle': 'subtle',
11
+ 'interactive-border': 'border',
12
+ 'interactive-hover': 'hover',
13
+ 'interactive-active': 'active',
14
+ 'interactive-selection': 'selection',
15
+ 'interactive-selection-foreground': 'selection-fg',
16
+ 'interactive-focus-ring': 'focus',
17
+ 'primary': 'primary',
18
+ 'primary-foreground': 'primary-fg',
19
+ 'primary-text': 'primary-text',
20
+ 'success-text': 'success-text',
21
+ 'warning-text': 'warning-text',
22
+ 'error-text': 'error-text',
23
+ 'info-text': 'info-text',
24
+ 'status-success': 'success',
25
+ 'status-warning': 'warning',
26
+ 'status-error': 'error',
27
+ 'status-info': 'info',
28
+ 'font-sans': 'font',
29
+ 'font-mono': 'mono',
30
+ 'radius': 'radius',
31
+ };
32
+ const v = (name, fallback) => (`var(--${name}, var(--oc-${OC_ALIAS[name]}, ${fallback}))`);
33
+ const bg = v('surface-background', 'transparent');
34
+ const elevated = v('surface-elevated', 'transparent');
35
+ const elevatedFg = v('surface-elevated-foreground', 'inherit');
36
+ const fg = v('surface-foreground', 'inherit');
37
+ const muted = v('surface-muted-foreground', 'gray');
38
+ const secondary = v('surface-muted', 'transparent');
39
+ const border = v('interactive-border', 'currentColor');
40
+ const hover = v('interactive-hover', 'transparent');
41
+ const active = v('interactive-active', 'transparent');
42
+ const selection = v('interactive-selection', 'transparent');
43
+ const selectionFg = v('interactive-selection-foreground', 'inherit');
44
+ const focus = v('interactive-focus-ring', 'currentColor');
45
+ const primary = v('primary', 'currentColor');
46
+ const primaryText = v('primary-text', 'inherit');
47
+ const errorText = v('error-text', 'inherit');
48
+ const font = v('font-sans', 'inherit');
49
+ const mono = v('font-mono', 'monospace');
50
+ const radius = v('radius', '9px');
51
+ const mix = (color, pct, base = 'transparent') => (`color-mix(in srgb, ${color} ${pct}%, ${base})`);
52
+ const focusRing = `box-shadow: 0 0 0 2px ${focus};`;
53
+ const tone = (name) => {
54
+ const color = v(`status-${name}`, 'currentColor');
55
+ return `
56
+ .oc-sdk[data-tone="${name}"], .oc-sdk [data-tone="${name}"] { --oc-sdk-tone: ${color}; --oc-sdk-tone-text: ${v(`${name}-text`, 'inherit')}; }`;
57
+ };
58
+ export const UI_CSS = `
59
+ ${GUEST_SCROLLBAR_CSS}
60
+ .oc-sdk { box-sizing: border-box; color: ${fg}; font-family: ${font}; font-size: 0.875rem; line-height: 1.45; }
61
+ .oc-sdk *, .oc-sdk *::before, .oc-sdk *::after { box-sizing: border-box; }
62
+ /* :where() keeps the reset at zero specificity so every primitive class below overrides it. */
63
+ :where(.oc-sdk) :where(button, input, textarea), :where(button.oc-sdk, input.oc-sdk, textarea.oc-sdk) { font: inherit; color: inherit; margin: 0; }
64
+ :where(.oc-sdk) :where(button), :where(button.oc-sdk) { cursor: pointer; background: none; border: 0; padding: 0; }
65
+ .oc-sdk button:disabled, button.oc-sdk:disabled, .oc-sdk[aria-disabled="true"], .oc-sdk [aria-disabled="true"] { opacity: .5; pointer-events: none; }
66
+ .oc-sdk :focus-visible { outline: none; ${focusRing} }
67
+ .oc-sdk-mono { font-family: ${mono}; }
68
+ .oc-sdk-muted { color: ${muted}; }
69
+ ${tone('success')}${tone('warning')}${tone('error')}${tone('info')}
70
+ .oc-sdk[data-tone="primary"], .oc-sdk [data-tone="primary"] { --oc-sdk-tone: ${primary}; --oc-sdk-tone-text: ${primaryText}; }
71
+
72
+ .oc-sdk-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 36px; padding: 0 14px; border: 1px solid transparent; border-radius: ${radius}; font-size: 0.875rem; font-weight: 500; line-height: 1; white-space: nowrap; transition: background 150ms ease-out, color 150ms ease-out; }
73
+ .oc-sdk-btn[data-size="sm"] { height: 32px; padding: 0 10px; font-size: 0.8125rem; }
74
+ .oc-sdk-btn[data-size="xs"] { height: 24px; padding: 0 8px; font-size: 0.75rem; border-radius: 6px; }
75
+ .oc-sdk-btn[data-variant="default"] { color: ${primaryText}; background: ${mix(primary, 10, bg)}; border-color: ${mix(primary, 12)}; }
76
+ .oc-sdk-btn[data-variant="default"]:hover { background: ${mix(primary, 16, bg)}; }
77
+ .oc-sdk-btn[data-variant="default"]:active { background: ${mix(primary, 22, bg)}; }
78
+ .oc-sdk-btn[data-variant="secondary"] { background: ${secondary}; color: var(--oc-fg); }
79
+ .oc-sdk-btn[data-variant="secondary"]:hover { background-image: linear-gradient(${hover}, ${hover}); }
80
+ .oc-sdk-btn[data-variant="secondary"]:active { background-image: linear-gradient(${active}, ${active}); }
81
+ .oc-sdk-btn[data-variant="outline"] { background: ${elevated}; color: ${elevatedFg}; border-color: ${border}; }
82
+ .oc-sdk-btn[data-variant="outline"]:hover { background-image: linear-gradient(${hover}, ${hover}); }
83
+ .oc-sdk-btn[data-variant="outline"]:active { background-image: linear-gradient(${active}, ${active}); }
84
+ .oc-sdk-btn[data-variant="ghost"] { background: transparent; }
85
+ .oc-sdk-btn[data-variant="ghost"]:hover { background: ${hover}; }
86
+ .oc-sdk-btn[data-variant="ghost"]:active { background: ${active}; }
87
+ .oc-sdk-btn[data-variant="destructive"] { --oc-sdk-tone: ${v('status-error', 'red')}; color: ${errorText}; background: ${mix('var(--oc-sdk-tone)', 7, bg)}; border-color: ${mix('var(--oc-sdk-tone)', 12)}; }
88
+ .oc-sdk-btn[data-variant="destructive"]:hover { background: ${mix('var(--oc-sdk-tone)', 9, bg)}; }
89
+ .oc-sdk-btn[data-variant="destructive"]:active { background: ${mix('var(--oc-sdk-tone)', 11, bg)}; }
90
+ .oc-sdk-btn[data-loading="true"] { opacity: .5; pointer-events: none; }
91
+ .oc-sdk-btn > .oc-sdk-spinner-ring { width: 14px; height: 14px; }
92
+
93
+ .oc-sdk-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
94
+ .oc-sdk-field-label { font-size: 0.8125rem; font-weight: 500; }
95
+ .oc-sdk-field-note { font-size: 0.75rem; color: ${muted}; }
96
+ .oc-sdk-field[data-invalid="true"] .oc-sdk-field-note { color: ${errorText}; }
97
+ .oc-sdk-input { display: block; width: 100%; min-width: 0; height: 36px; padding: 0 12px; border: 0; border-radius: ${radius}; background: ${elevated}; color: ${elevatedFg}; font-size: 0.875rem; line-height: 1.45; appearance: none; box-shadow: inset 0 0 0 1px ${mix(border, 60)}; transition: background 150ms ease-out, box-shadow 150ms ease-out; }
98
+ textarea.oc-sdk-input { height: auto; padding: 8px 12px; resize: vertical; }
99
+ .oc-sdk-input::placeholder { color: ${muted}; }
100
+ .oc-sdk-input:hover:not(:focus) { background-image: linear-gradient(${hover}, ${hover}); }
101
+ .oc-sdk-input:focus, .oc-sdk-input:focus-visible { box-shadow: inset 0 0 0 2px ${focus}; }
102
+ .oc-sdk-field[data-invalid="true"] .oc-sdk-input { box-shadow: inset 0 0 0 1px ${v('status-error', 'red')}; }
103
+ .oc-sdk-field[data-invalid="true"] .oc-sdk-input:focus { box-shadow: inset 0 0 0 2px ${v('status-error', 'red')}; }
104
+ .oc-sdk-input[data-mono="true"] { font-family: ${mono}; }
105
+
106
+ .oc-sdk-search { position: relative; min-width: 0; }
107
+ .oc-sdk-search .oc-sdk-input { padding-left: 34px; padding-right: 34px; }
108
+ .oc-sdk-search-icon { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: ${muted}; pointer-events: none; }
109
+ .oc-sdk-search[data-active="true"] .oc-sdk-search-icon { color: ${primary}; }
110
+ .oc-sdk-search-clear { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); display: none; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 6px; color: ${muted}; }
111
+ .oc-sdk-search[data-active="true"] .oc-sdk-search-clear { display: inline-flex; }
112
+ .oc-sdk-search-clear:hover { background: ${hover}; color: ${fg}; }
113
+
114
+ .oc-sdk-select { position: relative; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
115
+ .oc-sdk-trigger { display: inline-flex; align-items: center; gap: 6px; width: 100%; min-width: 0; height: 32px; padding: 0 8px 0 10px; border: 1px solid ${border}; border-radius: 6px; background: ${elevated}; color: ${elevatedFg}; font-size: 0.8125rem; text-align: left; transition: background 150ms ease-out; }
116
+ .oc-sdk-trigger:hover { background-image: linear-gradient(${hover}, ${hover}); }
117
+ .oc-sdk-trigger[aria-expanded="true"] { background-image: linear-gradient(${active}, ${active}); }
118
+ .oc-sdk-trigger-value { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
119
+ .oc-sdk-trigger-value[data-empty="true"] { color: ${muted}; }
120
+ .oc-sdk-trigger-chevron { flex: 0 0 auto; color: ${muted}; }
121
+ .oc-sdk-popup { --surface-foreground: ${elevatedFg}; position: fixed; z-index: 50; display: flex; flex-direction: column; gap: 2px; min-width: 160px; max-width: calc(100vw - 16px); max-height: min(320px, calc(100vh - 16px)); overflow: auto; padding: 4px; border: 1px solid ${mix(border, 60)}; border-radius: 12px; background: ${elevated}; color: ${elevatedFg}; box-shadow: 0 8px 24px ${mix(fg, 12)}; }
122
+ .oc-sdk-popup-search { flex: 0 0 auto; padding: 2px 2px 4px; }
123
+ .oc-sdk-popup-search .oc-sdk-input { height: 32px; font-size: 0.8125rem; }
124
+ .oc-sdk-option { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border-radius: 8px; font-size: 0.8125rem; text-align: left; }
125
+ .oc-sdk-option[data-active="true"] { background: ${hover}; }
126
+ .oc-sdk-option[aria-selected="true"] { background: ${selection}; color: ${selectionFg}; }
127
+ .oc-sdk-option[data-destructive="true"] { color: ${errorText}; }
128
+ .oc-sdk-option[data-destructive="true"][data-active="true"] { background: ${mix(v('status-error', 'red'), 10)}; }
129
+ .oc-sdk-option-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
130
+ .oc-sdk-option-hint { flex: 0 0 auto; font-size: 0.75rem; color: ${muted}; }
131
+ .oc-sdk-option-check { flex: 0 0 auto; width: 12px; }
132
+ .oc-sdk-popup-empty { padding: 8px; font-size: 0.8125rem; color: ${muted}; }
133
+
134
+ .oc-sdk-check { display: inline-flex; align-items: flex-start; gap: 8px; width: 100%; text-align: left; }
135
+ .oc-sdk-check-box { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; margin-top: 3px; border: 1px solid ${border}; border-radius: 4px; color: ${primary}; transition: border-color 150ms ease-out; }
136
+ .oc-sdk-check[aria-checked="true"] .oc-sdk-check-box { border-color: ${mix(primary, 65, border)}; }
137
+ .oc-sdk-check-box > svg { display: none; }
138
+ .oc-sdk-check[aria-checked="true"] .oc-sdk-check-box > svg { display: block; }
139
+ .oc-sdk-check-thumb { flex: 0 0 auto; position: relative; width: 36px; height: 20px; border-radius: 9999px; background: ${border}; transition: background 150ms ease-out; }
140
+ .oc-sdk-check-thumb::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 9999px; background: ${bg}; transition: transform 150ms ease-out; }
141
+ .oc-sdk-check[aria-checked="true"] .oc-sdk-check-thumb { background: ${primary}; }
142
+ .oc-sdk-check[aria-checked="true"] .oc-sdk-check-thumb::after { transform: translateX(16px); }
143
+ .oc-sdk-check:focus-visible { box-shadow: none; }
144
+ .oc-sdk-check:focus-visible .oc-sdk-check-box, .oc-sdk-check:focus-visible .oc-sdk-check-thumb { ${focusRing} }
145
+ .oc-sdk-check-text { display: flex; flex-direction: column; min-width: 0; }
146
+ .oc-sdk-check-label { font-size: 0.875rem; }
147
+ .oc-sdk-check-desc { font-size: 0.75rem; color: ${muted}; }
148
+
149
+ .oc-sdk-tabs { display: inline-flex; gap: 2px; padding: 2px; border-radius: 10px; max-width: 100%; overflow: auto; }
150
+ .oc-sdk-tabs[data-track="true"] { background: ${mix(fg, 4)}; }
151
+ .oc-sdk-tab { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px; border: 1px solid transparent; border-radius: 8px; font-size: 0.8125rem; font-weight: 500; color: ${muted}; white-space: nowrap; transition: color 150ms ease-out, background 150ms ease-out; }
152
+ .oc-sdk-tab:hover { color: ${fg}; }
153
+ .oc-sdk-tab[aria-selected="true"] { color: ${selectionFg}; background: ${selection}; border-color: ${border}; }
154
+ .oc-sdk-tab-count { font-size: 0.75rem; font-variant-numeric: tabular-nums; color: ${muted}; }
155
+
156
+ .oc-sdk-badge { display: inline-flex; align-items: center; padding: 1px 6px; border-radius: 9999px; font-size: 11px; font-weight: 500; line-height: 16px; white-space: nowrap; background: ${hover}; color: ${muted}; }
157
+ .oc-sdk-badge[data-tone] { color: var(--oc-sdk-tone-text, var(--oc-sdk-tone)); background: ${mix('var(--oc-sdk-tone)', 15)}; }
158
+
159
+ .oc-sdk-list { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
160
+ .oc-sdk-row { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 8px; border-radius: 6px; text-align: left; transition: background 120ms ease-out; }
161
+ .oc-sdk-row:hover, .oc-sdk-row[data-active="true"] { background: ${hover}; }
162
+ .oc-sdk-row[aria-selected="true"] { background: ${selection}; color: ${selectionFg}; }
163
+ .oc-sdk-row-lead { flex: 0 0 auto; width: 64px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-family: ${mono}; font-size: 0.75rem; color: ${muted}; }
164
+ .oc-sdk-row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
165
+ .oc-sdk-row-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
166
+ .oc-sdk-row-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.75rem; color: ${muted}; }
167
+ .oc-sdk-row-meta { flex: 0 0 auto; font-size: 0.75rem; font-variant-numeric: tabular-nums; color: ${muted}; }
168
+ .oc-sdk-row[aria-selected="true"] .oc-sdk-row-lead, .oc-sdk-row[aria-selected="true"] .oc-sdk-row-sub, .oc-sdk-row[aria-selected="true"] .oc-sdk-row-meta { color: inherit; opacity: .75; }
169
+ .oc-sdk-list-empty { padding: 16px 8px; text-align: center; font-size: 0.8125rem; color: ${muted}; }
170
+
171
+ .oc-sdk-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 40px 16px; text-align: center; }
172
+ .oc-sdk-empty-title { margin: 0; font-size: 0.8125rem; font-weight: 600; }
173
+ .oc-sdk-empty-body { margin: 0; max-width: 32rem; font-size: 0.8125rem; color: ${muted}; }
174
+ .oc-sdk-empty-action { margin-top: 12px; }
175
+
176
+ @keyframes oc-sdk-spin { to { transform: rotate(360deg); } }
177
+ .oc-sdk-spinner { display: inline-flex; align-items: center; gap: 8px; font-size: 0.8125rem; color: ${muted}; }
178
+ .oc-sdk-spinner-ring { width: 16px; height: 16px; border: 2px solid ${border}; border-top-color: ${primary}; border-radius: 9999px; animation: oc-sdk-spin .8s linear infinite; }
179
+ .oc-sdk-spinner[data-size="sm"] .oc-sdk-spinner-ring { width: 12px; height: 12px; }
180
+
181
+ .oc-sdk-banner { display: flex; align-items: flex-start; gap: 12px; padding: 8px 12px; border: 1px solid ${mix('var(--oc-sdk-tone)', 40)}; border-radius: 8px; background: ${mix('var(--oc-sdk-tone)', 10)}; }
182
+ .oc-sdk-banner-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
183
+ .oc-sdk-banner-title { font-size: 0.8125rem; font-weight: 500; color: var(--oc-sdk-tone-text, var(--oc-sdk-tone)); }
184
+ .oc-sdk-banner-body { font-size: 0.8125rem; color: ${muted}; }
185
+ .oc-sdk-banner-action { flex: 0 0 auto; }
186
+
187
+ .oc-sdk-separator { display: flex; align-items: center; gap: 8px; width: 100%; margin: 8px 0; font-size: 0.75rem; color: ${muted}; }
188
+ .oc-sdk-separator::before, .oc-sdk-separator::after { content: ""; flex: 1 1 auto; height: 1px; background: ${mix(border, 40)}; }
189
+ .oc-sdk-separator[data-labeled="false"]::after { display: none; }
190
+ .oc-sdk-popup > .oc-sdk-separator { margin: 4px 0; }
191
+
192
+ .oc-sdk-progress { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
193
+ .oc-sdk-progress-label { display: flex; justify-content: space-between; font-size: 0.75rem; color: ${muted}; font-variant-numeric: tabular-nums; }
194
+ .oc-sdk-progress-track { height: 6px; border-radius: 9999px; background: ${border}; overflow: hidden; }
195
+ .oc-sdk-progress-fill { height: 100%; border-radius: 9999px; background: var(--oc-sdk-tone, ${primary}); transform-origin: left; transition: transform 200ms ease-out; }
196
+
197
+ .oc-sdk-menu { position: relative; display: inline-flex; }
198
+
199
+ .oc-sdk-text { white-space: pre-wrap; overflow-wrap: anywhere; }
200
+ .oc-sdk-text a { color: ${primaryText}; text-decoration: underline; text-underline-offset: 2px; }
201
+ .oc-sdk-text img { display: block; max-width: 100%; margin: 8px 0; border-radius: 8px; border: 1px solid ${mix(border, 60)}; }
202
+ `;
@@ -0,0 +1,15 @@
1
+ import { type Handle } from './dom.ts';
2
+ export type TabItem = {
3
+ id: string;
4
+ label: string;
5
+ count?: number;
6
+ };
7
+ export type TabsProps = {
8
+ items: TabItem[];
9
+ activeId: string;
10
+ onChange: (id: string) => void;
11
+ /** Paints a faint track behind the pills. */
12
+ trackBackground?: boolean;
13
+ };
14
+ export type TabsHandle = Handle<TabsProps>;
15
+ export declare const mountTabs: (root: Element, initial: TabsProps) => TabsHandle;
@@ -0,0 +1,61 @@
1
+ import { button, clearNode, el, ensureStyle } from "./dom.js";
2
+ import { moveListSelection, navigationKey } from "./navigation.js";
3
+ import { UI_CSS } from "./style.js";
4
+ export const mountTabs = (root, initial) => {
5
+ ensureStyle(UI_CSS);
6
+ let props = initial;
7
+ const track = el('div', 'oc-sdk oc-sdk-tabs');
8
+ track.setAttribute('role', 'tablist');
9
+ root.append(track);
10
+ const paint = () => {
11
+ clearNode(track);
12
+ track.dataset.track = props.trackBackground ? 'true' : 'false';
13
+ for (const item of props.items) {
14
+ const tab = button('oc-sdk-tab');
15
+ tab.setAttribute('role', 'tab');
16
+ const active = item.id === props.activeId;
17
+ tab.setAttribute('aria-selected', active ? 'true' : 'false');
18
+ tab.tabIndex = active ? 0 : -1;
19
+ tab.dataset.id = item.id;
20
+ const label = el('span');
21
+ label.textContent = item.label;
22
+ tab.append(label);
23
+ if (item.count !== undefined) {
24
+ const count = el('span', 'oc-sdk-tab-count');
25
+ count.textContent = String(item.count);
26
+ tab.append(count);
27
+ }
28
+ tab.addEventListener('click', () => {
29
+ if (item.id !== props.activeId)
30
+ props.onChange(item.id);
31
+ });
32
+ track.append(tab);
33
+ }
34
+ };
35
+ const onKeyDown = (event) => {
36
+ const step = navigationKey(event, 'horizontal');
37
+ if (!step) {
38
+ return;
39
+ }
40
+ const next = moveListSelection(props.items, props.activeId, step);
41
+ if (next && next !== props.activeId) {
42
+ event.preventDefault();
43
+ props.onChange(next);
44
+ const tab = track.querySelector(`[data-id="${CSS.escape(next)}"]`);
45
+ if (tab instanceof HTMLElement)
46
+ tab.focus();
47
+ }
48
+ };
49
+ track.addEventListener('keydown', onKeyDown);
50
+ paint();
51
+ return {
52
+ update: (next) => {
53
+ props = { ...props, ...next };
54
+ paint();
55
+ },
56
+ dispose: () => {
57
+ track.removeEventListener('keydown', onKeyDown);
58
+ track.remove();
59
+ },
60
+ };
61
+ };
@@ -0,0 +1,23 @@
1
+ import { type Handle } from './dom.ts';
2
+ export type TextPart = {
3
+ kind: 'text';
4
+ text: string;
5
+ } | {
6
+ kind: 'image';
7
+ src: string;
8
+ alt: string;
9
+ } | {
10
+ kind: 'link';
11
+ href: string;
12
+ label: string;
13
+ };
14
+ export type TextProps = {
15
+ text: string;
16
+ /** Called with the href of a clicked link. A sandboxed iframe cannot open it alone. */
17
+ onOpenUrl?: (url: string) => void;
18
+ };
19
+ export type TextHandle = Handle<TextProps>;
20
+ export declare const isHttpUrl: (value: string) => boolean;
21
+ /** Splits text into plain runs, `![alt](https://…)` images, and `[label](https://…)` links. Anything else stays text. */
22
+ export declare const splitTextMedia: (text: string) => TextPart[];
23
+ export declare const mountText: (root: Element, initial: TextProps) => TextHandle;
@@ -0,0 +1,89 @@
1
+ import { clearNode, el, ensureStyle } from "./dom.js";
2
+ import { UI_CSS } from "./style.js";
3
+ const MARKDOWN_TOKEN = /(!?)\[([^\]]*)\]\((https?:\/\/[^)\s]+)\)/g;
4
+ export const isHttpUrl = (value) => {
5
+ try {
6
+ const url = new URL(value);
7
+ return url.protocol === 'http:' || url.protocol === 'https:';
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ };
13
+ /** Splits text into plain runs, `![alt](https://…)` images, and `[label](https://…)` links. Anything else stays text. */
14
+ export const splitTextMedia = (text) => {
15
+ const parts = [];
16
+ let last = 0;
17
+ for (const match of text.matchAll(MARKDOWN_TOKEN)) {
18
+ const index = match.index ?? 0;
19
+ if (index > last) {
20
+ parts.push({ kind: 'text', text: text.slice(last, index) });
21
+ }
22
+ const marker = match[1] ?? '';
23
+ const label = (match[2] ?? '').trim();
24
+ const href = match[3] ?? '';
25
+ if (!isHttpUrl(href)) {
26
+ parts.push({ kind: 'text', text: match[0] });
27
+ }
28
+ else if (marker === '!') {
29
+ parts.push({ kind: 'image', src: href, alt: label });
30
+ }
31
+ else {
32
+ parts.push({ kind: 'link', href, label: label || href });
33
+ }
34
+ last = index + match[0].length;
35
+ }
36
+ if (last < text.length) {
37
+ parts.push({ kind: 'text', text: text.slice(last) });
38
+ }
39
+ return parts;
40
+ };
41
+ export const mountText = (root, initial) => {
42
+ ensureStyle(UI_CSS);
43
+ let props = initial;
44
+ const node = el('div', 'oc-sdk oc-sdk-text');
45
+ root.append(node);
46
+ const onClick = (event) => {
47
+ if (!(event.target instanceof HTMLAnchorElement) || !props.onOpenUrl) {
48
+ return;
49
+ }
50
+ event.preventDefault();
51
+ props.onOpenUrl(event.target.href);
52
+ };
53
+ const paint = () => {
54
+ clearNode(node);
55
+ for (const part of splitTextMedia(props.text)) {
56
+ if (part.kind === 'text') {
57
+ node.append(document.createTextNode(part.text));
58
+ }
59
+ else if (part.kind === 'link') {
60
+ const link = el('a');
61
+ link.href = part.href;
62
+ link.rel = 'noopener noreferrer';
63
+ link.target = '_blank';
64
+ link.textContent = part.label;
65
+ node.append(link);
66
+ }
67
+ else {
68
+ const img = el('img');
69
+ img.src = part.src;
70
+ img.alt = part.alt;
71
+ img.loading = 'lazy';
72
+ img.referrerPolicy = 'no-referrer';
73
+ node.append(img);
74
+ }
75
+ }
76
+ };
77
+ node.addEventListener('click', onClick);
78
+ paint();
79
+ return {
80
+ update: (next) => {
81
+ props = { ...props, ...next };
82
+ paint();
83
+ },
84
+ dispose: () => {
85
+ node.removeEventListener('click', onClick);
86
+ node.remove();
87
+ },
88
+ };
89
+ };
@@ -0,0 +1,22 @@
1
+ import type { GuestHostSurface, HostTheme } from '../contract.ts';
2
+ export type ThemeRoot = {
3
+ style: {
4
+ colorScheme: string;
5
+ setProperty: (name: string, value: string) => void;
6
+ };
7
+ dataset?: {
8
+ ocSurface?: string;
9
+ ocTheme?: string;
10
+ };
11
+ };
12
+ /**
13
+ * Paint the host theme onto the iframe root. Guest chrome reads these
14
+ * variables, and the root itself gets the host font and text colour so plain
15
+ * DOM the guest draws outside the kit (a `<pre>`, a `<p>`) inherits them
16
+ * instead of the browser's serif default.
17
+ */
18
+ export declare const applyHostTheme: (theme: HostTheme, root: ThemeRoot) => void;
19
+ export declare const applyHostReady: (ctx: {
20
+ theme: HostTheme;
21
+ surface: GuestHostSurface;
22
+ }, root: ThemeRoot) => void;
@@ -0,0 +1,79 @@
1
+ const TOKEN_VARS = [
2
+ ['--oc-bg', 'background'],
3
+ ['--oc-elevated', 'elevated'],
4
+ ['--oc-fg', 'foreground'],
5
+ ['--oc-muted', 'muted'],
6
+ ['--oc-subtle', 'subtle'],
7
+ ['--oc-border', 'border'],
8
+ ['--oc-hover', 'hover'],
9
+ ['--oc-selection', 'selection'],
10
+ ['--oc-focus', 'focus'],
11
+ ['--oc-primary', 'primary'],
12
+ ['--oc-muted-surface', 'mutedSurface'],
13
+ ['--oc-elevated-fg', 'elevatedForeground'],
14
+ ['--oc-active', 'active'],
15
+ ['--oc-selection-fg', 'selectionForeground'],
16
+ ['--oc-primary-fg', 'primaryForeground'],
17
+ ['--oc-primary-text', 'primaryText'],
18
+ ['--oc-success-text', 'successText'],
19
+ ['--oc-warning-text', 'warningText'],
20
+ ['--oc-error-text', 'errorText'],
21
+ ['--oc-info-text', 'infoText'],
22
+ ['--oc-success', 'success'],
23
+ ['--oc-warning', 'warning'],
24
+ ['--oc-error', 'error'],
25
+ ['--oc-info', 'info'],
26
+ ['--oc-font', 'font'],
27
+ ['--oc-mono', 'mono'],
28
+ ['--oc-radius', 'radius'],
29
+ ['--surface-background', 'background'],
30
+ ['--surface-elevated', 'elevated'],
31
+ ['--surface-foreground', 'foreground'],
32
+ ['--surface-muted-foreground', 'muted'],
33
+ ['--surface-subtle', 'subtle'],
34
+ ['--interactive-border', 'border'],
35
+ ['--interactive-hover', 'hover'],
36
+ ['--interactive-selection', 'selection'],
37
+ ['--interactive-focus-ring', 'focus'],
38
+ ['--primary', 'primary'],
39
+ ['--surface-muted', 'mutedSurface'],
40
+ ['--surface-elevated-foreground', 'elevatedForeground'],
41
+ ['--interactive-active', 'active'],
42
+ ['--interactive-selection-foreground', 'selectionForeground'],
43
+ ['--primary-foreground', 'primaryForeground'],
44
+ ['--primary-text', 'primaryText'],
45
+ ['--success-text', 'successText'],
46
+ ['--warning-text', 'warningText'],
47
+ ['--error-text', 'errorText'],
48
+ ['--info-text', 'infoText'],
49
+ ['--status-success', 'success'],
50
+ ['--status-warning', 'warning'],
51
+ ['--status-error', 'error'],
52
+ ['--status-info', 'info'],
53
+ ['--font-sans', 'font'],
54
+ ['--font-mono', 'mono'],
55
+ ['--radius', 'radius'],
56
+ ];
57
+ /**
58
+ * Paint the host theme onto the iframe root. Guest chrome reads these
59
+ * variables, and the root itself gets the host font and text colour so plain
60
+ * DOM the guest draws outside the kit (a `<pre>`, a `<p>`) inherits them
61
+ * instead of the browser's serif default.
62
+ */
63
+ export const applyHostTheme = (theme, root) => {
64
+ root.style.colorScheme = theme.mode;
65
+ for (const [name, key] of TOKEN_VARS) {
66
+ root.style.setProperty(name, theme.tokens[key]);
67
+ }
68
+ root.style.setProperty('font-family', theme.tokens.font);
69
+ root.style.setProperty('font-size', '0.875rem');
70
+ root.style.setProperty('line-height', '1.45');
71
+ root.style.setProperty('color', theme.tokens.foreground);
72
+ };
73
+ export const applyHostReady = (ctx, root) => {
74
+ applyHostTheme(ctx.theme, root);
75
+ if (root.dataset) {
76
+ root.dataset.ocSurface = ctx.surface;
77
+ root.dataset.ocTheme = ctx.theme.mode;
78
+ }
79
+ };
@@ -0,0 +1,136 @@
1
+ import { z } from 'zod';
2
+ export declare const guestWorkspaceQuerySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3
+ kind: z.ZodLiteral<"projects">;
4
+ }, z.core.$strict>, z.ZodObject<{
5
+ kind: z.ZodLiteral<"worktrees">;
6
+ projectId: z.ZodString;
7
+ }, z.core.$strict>, z.ZodObject<{
8
+ kind: z.ZodLiteral<"sessions">;
9
+ projectId: z.ZodString;
10
+ }, z.core.$strict>], "kind">;
11
+ export declare const guestWorkspaceSnapshotSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
12
+ kind: z.ZodLiteral<"projects">;
13
+ state: z.ZodEnum<{
14
+ loading: "loading";
15
+ ready: "ready";
16
+ error: "error";
17
+ }>;
18
+ projects: z.ZodArray<z.ZodObject<{
19
+ id: z.ZodString;
20
+ name: z.ZodString;
21
+ directory: z.ZodString;
22
+ }, z.core.$strip>>;
23
+ }, z.core.$strip>, z.ZodObject<{
24
+ kind: z.ZodLiteral<"worktrees">;
25
+ projectId: z.ZodString;
26
+ state: z.ZodEnum<{
27
+ loading: "loading";
28
+ ready: "ready";
29
+ error: "error";
30
+ }>;
31
+ worktrees: z.ZodArray<z.ZodObject<{
32
+ directory: z.ZodString;
33
+ name: z.ZodString;
34
+ branch: z.ZodString;
35
+ status: z.ZodEnum<{
36
+ ready: "ready";
37
+ pending: "pending";
38
+ invalid: "invalid";
39
+ missing: "missing";
40
+ }>;
41
+ }, z.core.$strip>>;
42
+ }, z.core.$strip>, z.ZodObject<{
43
+ kind: z.ZodLiteral<"sessions">;
44
+ projectId: z.ZodString;
45
+ state: z.ZodEnum<{
46
+ loading: "loading";
47
+ ready: "ready";
48
+ error: "error";
49
+ }>;
50
+ coverage: z.ZodArray<z.ZodObject<{
51
+ directory: z.ZodString;
52
+ state: z.ZodEnum<{
53
+ loading: "loading";
54
+ ready: "ready";
55
+ error: "error";
56
+ }>;
57
+ }, z.core.$strip>>;
58
+ sessions: z.ZodArray<z.ZodObject<{
59
+ id: z.ZodString;
60
+ title: z.ZodString;
61
+ projectId: z.ZodString;
62
+ directory: z.ZodString;
63
+ parentId: z.ZodNullable<z.ZodString>;
64
+ createdAt: z.ZodNumber;
65
+ updatedAt: z.ZodNumber;
66
+ archivedAt: z.ZodNullable<z.ZodNumber>;
67
+ worktree: z.ZodNullable<z.ZodObject<{
68
+ directory: z.ZodString;
69
+ name: z.ZodString;
70
+ branch: z.ZodString;
71
+ status: z.ZodEnum<{
72
+ ready: "ready";
73
+ pending: "pending";
74
+ invalid: "invalid";
75
+ missing: "missing";
76
+ }>;
77
+ }, z.core.$strip>>;
78
+ activity: z.ZodEnum<{
79
+ unknown: "unknown";
80
+ idle: "idle";
81
+ running: "running";
82
+ retrying: "retrying";
83
+ "waiting-permission": "waiting-permission";
84
+ "waiting-question": "waiting-question";
85
+ }>;
86
+ outcome: z.ZodNullable<z.ZodEnum<{
87
+ completed: "completed";
88
+ failed: "failed";
89
+ }>>;
90
+ items: z.ZodArray<z.ZodObject<{
91
+ id: z.ZodString;
92
+ data: z.ZodOptional<z.ZodJSONSchema>;
93
+ }, z.core.$strip>>;
94
+ }, z.core.$strip>>;
95
+ }, z.core.$strip>], "kind">;
96
+ export declare const guestSessionWorktreeSchema: z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
97
+ kind: z.ZodLiteral<"existing">;
98
+ directory: z.ZodString;
99
+ }, z.core.$strict>, z.ZodObject<{
100
+ kind: z.ZodLiteral<"new">;
101
+ name: z.ZodOptional<z.ZodString>;
102
+ baseBranch: z.ZodOptional<z.ZodString>;
103
+ }, z.core.$strict>]>;
104
+ export declare const guestStorageRequestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
105
+ op: z.ZodLiteral<"get">;
106
+ key: z.ZodString;
107
+ }, z.core.$strict>, z.ZodObject<{
108
+ op: z.ZodLiteral<"delete">;
109
+ key: z.ZodString;
110
+ }, z.core.$strict>, z.ZodObject<{
111
+ op: z.ZodLiteral<"set">;
112
+ key: z.ZodString;
113
+ value: z.ZodJSONSchema;
114
+ }, z.core.$strict>, z.ZodObject<{
115
+ op: z.ZodLiteral<"keys">;
116
+ }, z.core.$strict>], "op">;
117
+ export declare const guestStorageResultSchema: z.ZodUnion<readonly [z.ZodObject<{
118
+ storage: z.ZodLiteral<true>;
119
+ op: z.ZodLiteral<"get">;
120
+ found: z.ZodLiteral<false>;
121
+ }, z.core.$strip>, z.ZodObject<{
122
+ storage: z.ZodLiteral<true>;
123
+ op: z.ZodLiteral<"get">;
124
+ found: z.ZodLiteral<true>;
125
+ value: z.ZodJSONSchema;
126
+ }, z.core.$strip>, z.ZodObject<{
127
+ storage: z.ZodLiteral<true>;
128
+ op: z.ZodEnum<{
129
+ delete: "delete";
130
+ set: "set";
131
+ }>;
132
+ }, z.core.$strip>, z.ZodObject<{
133
+ storage: z.ZodLiteral<true>;
134
+ op: z.ZodLiteral<"keys">;
135
+ keys: z.ZodArray<z.ZodString>;
136
+ }, z.core.$strip>]>;