@womp/mcp-ui 0.1.18 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +9 -9
- package/dist/openai-camera/index.html +423 -173
- package/package.json +1 -1
- package/src/openai-camera/index.html +416 -166
- package/src/openai-camera/main.ts +295 -53
|
@@ -5,42 +5,131 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Camera</title>
|
|
7
7
|
<style>
|
|
8
|
+
/*
|
|
9
|
+
* Every colour, size and radius comes from the host first (ChatGPT sends
|
|
10
|
+
* its own over the bridge) and falls back to these. Names stay local so
|
|
11
|
+
* the rules below read as design, not plumbing.
|
|
12
|
+
*/
|
|
8
13
|
:root {
|
|
9
14
|
color-scheme: light dark;
|
|
15
|
+
--fg: var(--color-text-primary, #18181b);
|
|
16
|
+
--fg-2: var(--color-text-secondary, #6b6b70);
|
|
17
|
+
--bg: var(--color-background-primary, #ffffff);
|
|
18
|
+
--bg-2: var(--color-background-secondary, #f6f6f7);
|
|
19
|
+
--line: var(--color-border-primary, rgba(0, 0, 0, 0.08));
|
|
20
|
+
--line-2: var(--color-border-secondary, rgba(0, 0, 0, 0.14));
|
|
21
|
+
--radius: var(--border-radius-xl, 20px);
|
|
22
|
+
--radius-sm: var(--border-radius-md, 12px);
|
|
23
|
+
--pill: var(--border-radius-full, 999px);
|
|
24
|
+
--text-xs: var(--font-text-xs-size, 12px);
|
|
25
|
+
--text-sm: var(--font-text-sm-size, 14px);
|
|
26
|
+
--text-md: var(--font-text-md-size, 16px);
|
|
27
|
+
--stage-a: #f4f4f5;
|
|
28
|
+
--stage-b: #e4e4e7;
|
|
10
29
|
}
|
|
30
|
+
@media (prefers-color-scheme: dark) {
|
|
31
|
+
:root:not([data-theme="light"]) {
|
|
32
|
+
--fg: var(--color-text-primary, #f4f4f5);
|
|
33
|
+
--fg-2: var(--color-text-secondary, #a1a1aa);
|
|
34
|
+
--bg: var(--color-background-primary, #1c1c1f);
|
|
35
|
+
--bg-2: var(--color-background-secondary, #26262a);
|
|
36
|
+
--line: var(--color-border-primary, rgba(255, 255, 255, 0.08));
|
|
37
|
+
--line-2: var(--color-border-secondary, rgba(255, 255, 255, 0.16));
|
|
38
|
+
--stage-a: #2a2a2e;
|
|
39
|
+
--stage-b: #1a1a1d;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
:root[data-theme="dark"] {
|
|
43
|
+
--fg: var(--color-text-primary, #f4f4f5);
|
|
44
|
+
--fg-2: var(--color-text-secondary, #a1a1aa);
|
|
45
|
+
--bg: var(--color-background-primary, #1c1c1f);
|
|
46
|
+
--bg-2: var(--color-background-secondary, #26262a);
|
|
47
|
+
--line: var(--color-border-primary, rgba(255, 255, 255, 0.08));
|
|
48
|
+
--line-2: var(--color-border-secondary, rgba(255, 255, 255, 0.16));
|
|
49
|
+
--stage-a: #2a2a2e;
|
|
50
|
+
--stage-b: #1a1a1d;
|
|
51
|
+
}
|
|
52
|
+
|
|
11
53
|
body {
|
|
12
54
|
margin: 0;
|
|
13
|
-
font-family: var(--font-sans, system-ui, -apple-system, "Segoe UI", sans-serif);
|
|
14
|
-
color: var(--
|
|
55
|
+
font-family: var(--font-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif);
|
|
56
|
+
color: var(--fg);
|
|
57
|
+
-webkit-font-smoothing: antialiased;
|
|
58
|
+
}
|
|
59
|
+
[hidden] { display: none !important; }
|
|
60
|
+
button { font: inherit; color: inherit; }
|
|
61
|
+
button:focus-visible, input:focus-visible, summary:focus-visible {
|
|
62
|
+
outline: 2px solid var(--fg);
|
|
63
|
+
outline-offset: 2px;
|
|
15
64
|
}
|
|
16
65
|
|
|
66
|
+
/*
|
|
67
|
+
* One card, two layouts. Inline: render, colours, price, Customize and
|
|
68
|
+
* Order. Fullscreen: the same card with every control, in a panel beside
|
|
69
|
+
* (wide) or under (narrow) the render. `.full-only` is the editing the
|
|
70
|
+
* inline card leaves to fullscreen.
|
|
71
|
+
*/
|
|
72
|
+
:root:not([data-display-mode="fullscreen"]) .full-only { display: none !important; }
|
|
73
|
+
:root[data-display-mode="fullscreen"] .inline-only { display: none !important; }
|
|
74
|
+
|
|
17
75
|
.card {
|
|
18
|
-
display:
|
|
19
|
-
|
|
20
|
-
max-width:
|
|
21
|
-
border-radius: var(--
|
|
76
|
+
display: grid;
|
|
77
|
+
grid-template-columns: minmax(0, 1fr);
|
|
78
|
+
max-width: 760px;
|
|
79
|
+
border-radius: var(--radius);
|
|
22
80
|
overflow: hidden;
|
|
23
|
-
background: var(--
|
|
24
|
-
border: 1px solid var(--
|
|
81
|
+
background: var(--bg);
|
|
82
|
+
border: 1px solid var(--line);
|
|
25
83
|
box-sizing: border-box;
|
|
26
84
|
}
|
|
27
|
-
/*
|
|
85
|
+
/* The chat column is wide enough on desktop: render left, choices right. */
|
|
86
|
+
@media (min-width: 560px) {
|
|
87
|
+
:root:not([data-display-mode="fullscreen"]) .card { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
|
|
88
|
+
:root:not([data-display-mode="fullscreen"]) .stage { height: auto; min-height: 324px; margin: 8px 0 8px 8px; }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Narrow fullscreen: render on top, controls scroll under it, Order pinned. */
|
|
28
92
|
:root[data-display-mode="fullscreen"] .card {
|
|
29
93
|
max-width: none;
|
|
30
94
|
height: 100vh;
|
|
31
95
|
border-radius: 0;
|
|
32
96
|
border: 0;
|
|
97
|
+
grid-template-rows: auto 40vh minmax(0, 1fr) auto;
|
|
98
|
+
}
|
|
99
|
+
:root[data-display-mode="fullscreen"] .stage { height: auto; margin: 12px 12px 0; border-radius: 16px; }
|
|
100
|
+
@media (min-width: 760px) {
|
|
101
|
+
:root[data-display-mode="fullscreen"] .stage { margin: 12px; }
|
|
102
|
+
}
|
|
103
|
+
/* Wide fullscreen: big render left, a fixed panel right. */
|
|
104
|
+
@media (min-width: 760px) {
|
|
105
|
+
:root[data-display-mode="fullscreen"] .card {
|
|
106
|
+
grid-template-columns: minmax(0, 1fr) 380px;
|
|
107
|
+
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
108
|
+
}
|
|
109
|
+
:root[data-display-mode="fullscreen"] .top { grid-column: 1 / -1; }
|
|
110
|
+
:root[data-display-mode="fullscreen"] .panel { border-left: 1px solid var(--line); }
|
|
33
111
|
}
|
|
34
112
|
|
|
113
|
+
.top {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 4px;
|
|
117
|
+
height: 56px;
|
|
118
|
+
padding: 0 16px 0 6px;
|
|
119
|
+
border-bottom: 1px solid var(--line);
|
|
120
|
+
}
|
|
121
|
+
.top strong { flex: 1; min-width: 0; font-size: var(--text-md); font-weight: 600; }
|
|
122
|
+
.top .muted { font-size: var(--text-sm); }
|
|
123
|
+
|
|
124
|
+
/* The render sits on a soft studio backdrop, never a flat grey box. */
|
|
125
|
+
/* The render is inset in the card with its own rounded frame. */
|
|
35
126
|
.stage {
|
|
36
127
|
position: relative;
|
|
37
|
-
height:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
height: auto;
|
|
43
|
-
min-height: 0;
|
|
128
|
+
height: 280px;
|
|
129
|
+
margin: 8px 8px 0;
|
|
130
|
+
border-radius: calc(var(--radius) - 8px);
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
background: radial-gradient(120% 90% at 50% 35%, var(--stage-a), var(--stage-b));
|
|
44
133
|
}
|
|
45
134
|
.stage iframe {
|
|
46
135
|
display: block;
|
|
@@ -48,82 +137,123 @@
|
|
|
48
137
|
height: 100%;
|
|
49
138
|
border: 0;
|
|
50
139
|
}
|
|
51
|
-
.
|
|
52
|
-
|
|
53
|
-
.fallback {
|
|
140
|
+
.preview {
|
|
54
141
|
position: absolute;
|
|
55
142
|
inset: 0;
|
|
56
|
-
overflow: auto;
|
|
57
|
-
padding: 16px;
|
|
58
|
-
box-sizing: border-box;
|
|
59
|
-
background: var(--color-background-tertiary, #ecebe6);
|
|
60
|
-
color: var(--color-text-primary, #1b1b1a);
|
|
61
|
-
font-size: var(--font-text-sm-size, 13px);
|
|
62
|
-
}
|
|
63
|
-
.fallback[hidden] { display: none; }
|
|
64
|
-
.fallback p { margin: 0 0 10px; }
|
|
65
|
-
.fallback dl {
|
|
66
143
|
display: grid;
|
|
67
|
-
|
|
68
|
-
|
|
144
|
+
place-items: center;
|
|
145
|
+
padding: 24px;
|
|
146
|
+
}
|
|
147
|
+
.preview svg { width: min(62%, 280px); height: auto; filter: drop-shadow(0 18px 22px rgba(0, 0, 0, 0.16)); }
|
|
148
|
+
.preview p {
|
|
149
|
+
position: absolute;
|
|
150
|
+
left: 0;
|
|
151
|
+
right: 0;
|
|
152
|
+
bottom: 12px;
|
|
69
153
|
margin: 0;
|
|
70
|
-
|
|
154
|
+
text-align: center;
|
|
155
|
+
font-size: var(--text-xs);
|
|
156
|
+
color: var(--fg-2);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.panel {
|
|
160
|
+
display: flex;
|
|
161
|
+
flex-direction: column;
|
|
162
|
+
min-height: 0;
|
|
163
|
+
min-width: 0;
|
|
164
|
+
}
|
|
165
|
+
.scroll {
|
|
166
|
+
flex: 1;
|
|
167
|
+
min-height: 0;
|
|
168
|
+
overflow: auto;
|
|
169
|
+
display: grid;
|
|
170
|
+
align-content: start;
|
|
171
|
+
gap: 18px;
|
|
172
|
+
padding: 18px 18px 6px;
|
|
71
173
|
}
|
|
72
|
-
.
|
|
73
|
-
|
|
174
|
+
:root[data-display-mode="fullscreen"] .scroll { gap: 0; padding: 0; }
|
|
175
|
+
|
|
176
|
+
.title { display: flex; align-items: baseline; gap: 12px; }
|
|
177
|
+
.title > div { flex: 1; min-width: 0; display: grid; gap: 3px; }
|
|
178
|
+
.title h2 { margin: 0; font-size: 18px; font-weight: 650; letter-spacing: -0.01em; }
|
|
179
|
+
.title .price-tag { font-size: var(--text-md); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
180
|
+
.muted { margin: 0; font-size: var(--text-sm); color: var(--fg-2); }
|
|
74
181
|
|
|
75
|
-
.
|
|
182
|
+
/* Which part the swatches paint. */
|
|
183
|
+
.parts {
|
|
76
184
|
display: grid;
|
|
185
|
+
grid-template-columns: 1fr 1fr;
|
|
186
|
+
gap: 4px;
|
|
187
|
+
padding: 4px;
|
|
188
|
+
border-radius: var(--pill);
|
|
189
|
+
background: var(--bg-2);
|
|
190
|
+
}
|
|
191
|
+
.parts button {
|
|
192
|
+
white-space: nowrap;
|
|
193
|
+
display: flex;
|
|
194
|
+
align-items: center;
|
|
195
|
+
justify-content: center;
|
|
77
196
|
gap: 8px;
|
|
78
|
-
|
|
197
|
+
min-height: 36px;
|
|
198
|
+
padding: 0 12px;
|
|
199
|
+
border: 0;
|
|
200
|
+
border-radius: var(--pill);
|
|
201
|
+
background: transparent;
|
|
202
|
+
color: var(--fg-2);
|
|
203
|
+
font-size: var(--text-sm);
|
|
204
|
+
font-weight: 500;
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
}
|
|
207
|
+
.parts button[aria-selected="true"] {
|
|
208
|
+
background: var(--bg);
|
|
209
|
+
color: var(--fg);
|
|
210
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 0 0 1px var(--line);
|
|
211
|
+
}
|
|
212
|
+
.dot {
|
|
213
|
+
width: 14px;
|
|
214
|
+
height: 14px;
|
|
215
|
+
border-radius: 50%;
|
|
216
|
+
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.14);
|
|
79
217
|
}
|
|
80
|
-
|
|
218
|
+
|
|
219
|
+
.group { display: grid; gap: 12px; min-width: 0; }
|
|
220
|
+
.group-head {
|
|
81
221
|
display: flex;
|
|
82
222
|
align-items: center;
|
|
83
223
|
gap: 10px;
|
|
84
|
-
min-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
flex: none;
|
|
88
|
-
width: 48px;
|
|
89
|
-
font-size: var(--font-text-xs-size, 12px);
|
|
90
|
-
color: var(--color-text-secondary, rgba(27, 27, 26, 0.62));
|
|
224
|
+
min-height: 28px;
|
|
225
|
+
font-size: var(--text-sm);
|
|
226
|
+
color: var(--fg-2);
|
|
91
227
|
}
|
|
228
|
+
.group-head > span { flex: 1; min-width: 0; }
|
|
229
|
+
.group-head b { color: var(--fg); font-weight: 600; }
|
|
230
|
+
|
|
92
231
|
.swatches {
|
|
93
|
-
display:
|
|
94
|
-
|
|
95
|
-
gap:
|
|
96
|
-
|
|
232
|
+
display: grid;
|
|
233
|
+
grid-template-columns: repeat(auto-fill, minmax(30px, 1fr));
|
|
234
|
+
gap: 10px;
|
|
235
|
+
justify-items: center;
|
|
97
236
|
}
|
|
98
|
-
.swatch {
|
|
99
|
-
|
|
100
|
-
|
|
237
|
+
.swatch, .pick, .icon-button.tool {
|
|
238
|
+
position: relative;
|
|
239
|
+
width: 30px;
|
|
240
|
+
height: 30px;
|
|
101
241
|
padding: 0;
|
|
102
242
|
border-radius: 50%;
|
|
103
|
-
border:
|
|
243
|
+
border: 0;
|
|
244
|
+
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
|
|
245
|
+
box-sizing: border-box;
|
|
104
246
|
cursor: pointer;
|
|
247
|
+
transition: transform 120ms ease;
|
|
105
248
|
}
|
|
249
|
+
.swatch:hover, .pick:hover { transform: scale(1.08); }
|
|
106
250
|
/* The clear resin gets a hint of glass rather than a flat swatch. */
|
|
107
251
|
.swatch.clear {
|
|
108
|
-
background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.
|
|
109
|
-
}
|
|
110
|
-
.swatch[aria-pressed="true"] {
|
|
111
|
-
outline: 2px solid var(--color-text-primary, #1b1b1a);
|
|
112
|
-
outline-offset: 2px;
|
|
252
|
+
background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0) 60%) !important;
|
|
113
253
|
}
|
|
114
|
-
|
|
254
|
+
.swatch[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--fg); }
|
|
115
255
|
/* Any colour: the native picker (Chromium's has an eyedropper of its own). */
|
|
116
|
-
.pick {
|
|
117
|
-
position: relative;
|
|
118
|
-
flex: none;
|
|
119
|
-
width: 22px;
|
|
120
|
-
height: 22px;
|
|
121
|
-
border-radius: 50%;
|
|
122
|
-
border: 1px solid rgba(0, 0, 0, 0.18);
|
|
123
|
-
background: conic-gradient(#ff5e5e, #ffd93d, #6bdc6b, #4fc3f7, #7b6cf6, #f06bd8, #ff5e5e);
|
|
124
|
-
cursor: pointer;
|
|
125
|
-
box-sizing: border-box;
|
|
126
|
-
}
|
|
256
|
+
.pick { background: conic-gradient(#ff5e5e, #ffd93d, #6bdc6b, #4fc3f7, #7b6cf6, #f06bd8, #ff5e5e); }
|
|
127
257
|
.pick input {
|
|
128
258
|
position: absolute;
|
|
129
259
|
inset: 0;
|
|
@@ -134,168 +264,288 @@
|
|
|
134
264
|
border: 0;
|
|
135
265
|
cursor: pointer;
|
|
136
266
|
}
|
|
267
|
+
|
|
137
268
|
.icon-button {
|
|
138
269
|
flex: none;
|
|
139
270
|
display: inline-grid;
|
|
140
271
|
place-items: center;
|
|
141
|
-
width:
|
|
142
|
-
height:
|
|
272
|
+
width: 32px;
|
|
273
|
+
height: 32px;
|
|
143
274
|
padding: 0;
|
|
144
275
|
border-radius: 50%;
|
|
145
|
-
border: 1px solid var(--
|
|
276
|
+
border: 1px solid var(--line-2);
|
|
146
277
|
background: transparent;
|
|
147
|
-
|
|
148
|
-
font: inherit;
|
|
149
|
-
font-size: 14px;
|
|
278
|
+
font-size: 16px;
|
|
150
279
|
line-height: 1;
|
|
151
280
|
cursor: pointer;
|
|
152
281
|
}
|
|
153
|
-
.icon-button
|
|
154
|
-
.icon-button
|
|
282
|
+
.icon-button.tool { border: 0; background: var(--bg-2); }
|
|
283
|
+
.icon-button.close { width: 44px; height: 44px; border: 0; }
|
|
284
|
+
.icon-button:hover:not(:disabled) { background: var(--bg-2); }
|
|
285
|
+
.icon-button:disabled { opacity: 0.35; cursor: default; }
|
|
155
286
|
|
|
156
|
-
.
|
|
157
|
-
flex: none;
|
|
287
|
+
.segmented {
|
|
158
288
|
display: inline-flex;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
padding: 3px 10px;
|
|
168
|
-
border: 0;
|
|
169
|
-
border-radius: var(--border-radius-full, 999px);
|
|
289
|
+
flex-wrap: wrap;
|
|
290
|
+
gap: 6px;
|
|
291
|
+
}
|
|
292
|
+
.segmented button {
|
|
293
|
+
min-height: 34px;
|
|
294
|
+
padding: 0 14px;
|
|
295
|
+
border-radius: var(--pill);
|
|
296
|
+
border: 1px solid var(--line-2);
|
|
170
297
|
background: transparent;
|
|
171
|
-
|
|
298
|
+
font-size: var(--text-sm);
|
|
172
299
|
cursor: pointer;
|
|
173
300
|
}
|
|
174
|
-
.
|
|
175
|
-
|
|
176
|
-
color:
|
|
301
|
+
.segmented button:hover { background: var(--bg-2); }
|
|
302
|
+
.segmented button[aria-pressed="true"] {
|
|
303
|
+
border-color: transparent;
|
|
304
|
+
background: var(--fg);
|
|
305
|
+
color: var(--bg);
|
|
306
|
+
}
|
|
307
|
+
.segmented.small { gap: 2px; padding: 2px; border-radius: var(--pill); background: var(--bg-2); }
|
|
308
|
+
.segmented.small button { min-height: 28px; padding: 0 12px; border: 0; font-size: var(--text-xs); }
|
|
309
|
+
.segmented.small button[aria-pressed="true"] {
|
|
310
|
+
background: var(--bg);
|
|
311
|
+
color: var(--fg);
|
|
312
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 0 0 1px var(--line);
|
|
177
313
|
}
|
|
178
314
|
|
|
179
315
|
/* A gradient: its colours on a track, the one being edited ringed. */
|
|
180
316
|
.stops {
|
|
181
317
|
display: flex;
|
|
182
318
|
align-items: center;
|
|
183
|
-
gap:
|
|
319
|
+
gap: 8px;
|
|
184
320
|
min-width: 0;
|
|
185
321
|
}
|
|
186
|
-
.stops[hidden] { display: none; }
|
|
187
322
|
.track {
|
|
188
323
|
position: relative;
|
|
189
|
-
|
|
190
|
-
height:
|
|
191
|
-
border-radius: var(--
|
|
192
|
-
|
|
193
|
-
box-sizing: border-box;
|
|
324
|
+
flex: 1;
|
|
325
|
+
height: 28px;
|
|
326
|
+
border-radius: var(--pill);
|
|
327
|
+
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
|
|
194
328
|
}
|
|
195
329
|
.stop {
|
|
196
330
|
position: absolute;
|
|
197
331
|
top: 50%;
|
|
198
|
-
width:
|
|
199
|
-
height:
|
|
200
|
-
margin: -
|
|
332
|
+
width: 22px;
|
|
333
|
+
height: 22px;
|
|
334
|
+
margin: -11px 0 0 -11px;
|
|
201
335
|
padding: 0;
|
|
202
336
|
border-radius: 50%;
|
|
203
|
-
border:
|
|
204
|
-
box-shadow: 0
|
|
337
|
+
border: 3px solid #ffffff;
|
|
338
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
205
339
|
cursor: pointer;
|
|
206
340
|
}
|
|
207
|
-
.stop[aria-pressed="true"] {
|
|
208
|
-
|
|
341
|
+
.stop[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--fg), 0 1px 3px rgba(0, 0, 0, 0.3); }
|
|
342
|
+
|
|
343
|
+
/* Fullscreen sections: one heading each, generous air between. */
|
|
344
|
+
.block { display: grid; gap: 14px; }
|
|
345
|
+
:root[data-display-mode="fullscreen"] .block { padding: 20px; }
|
|
346
|
+
#knobs .section { border-top: 1px solid var(--line); }
|
|
347
|
+
.block h3, .section h3 {
|
|
348
|
+
margin: 0;
|
|
349
|
+
font-size: var(--text-xs);
|
|
350
|
+
font-weight: 600;
|
|
351
|
+
letter-spacing: 0.04em;
|
|
352
|
+
text-transform: uppercase;
|
|
353
|
+
color: var(--fg-2);
|
|
209
354
|
}
|
|
355
|
+
.section { display: grid; gap: 18px; padding: 20px; }
|
|
210
356
|
|
|
211
|
-
.
|
|
357
|
+
.knob { display: grid; gap: 10px; min-width: 0; }
|
|
358
|
+
.knob-head {
|
|
212
359
|
display: flex;
|
|
360
|
+
justify-content: space-between;
|
|
361
|
+
gap: 10px;
|
|
362
|
+
font-size: var(--text-sm);
|
|
363
|
+
}
|
|
364
|
+
.knob-head output { color: var(--fg-2); font-variant-numeric: tabular-nums; }
|
|
365
|
+
/* A thin track and a round thumb, the same in every browser. */
|
|
366
|
+
.knob input[type="range"] {
|
|
367
|
+
-webkit-appearance: none;
|
|
368
|
+
appearance: none;
|
|
369
|
+
width: 100%;
|
|
370
|
+
height: 20px;
|
|
371
|
+
margin: 0;
|
|
372
|
+
background: transparent;
|
|
373
|
+
cursor: pointer;
|
|
374
|
+
--fill: 50%;
|
|
375
|
+
}
|
|
376
|
+
.knob input[type="range"]::-webkit-slider-runnable-track {
|
|
377
|
+
height: 4px;
|
|
378
|
+
border-radius: 2px;
|
|
379
|
+
background: linear-gradient(to right, var(--fg) var(--fill), var(--line-2) var(--fill));
|
|
380
|
+
}
|
|
381
|
+
.knob input[type="range"]::-moz-range-track { height: 4px; border-radius: 2px; background: var(--line-2); }
|
|
382
|
+
.knob input[type="range"]::-moz-range-progress { height: 4px; border-radius: 2px; background: var(--fg); }
|
|
383
|
+
.knob input[type="range"]::-webkit-slider-thumb {
|
|
384
|
+
-webkit-appearance: none;
|
|
385
|
+
width: 20px;
|
|
386
|
+
height: 20px;
|
|
387
|
+
margin-top: -8px;
|
|
388
|
+
border-radius: 50%;
|
|
389
|
+
background: #ffffff;
|
|
390
|
+
border: 0;
|
|
391
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.2);
|
|
392
|
+
}
|
|
393
|
+
.knob input[type="range"]::-moz-range-thumb {
|
|
394
|
+
width: 20px;
|
|
395
|
+
height: 20px;
|
|
396
|
+
border-radius: 50%;
|
|
397
|
+
background: #ffffff;
|
|
398
|
+
border: 0;
|
|
399
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.2);
|
|
400
|
+
}
|
|
401
|
+
details.more > summary {
|
|
402
|
+
list-style: none;
|
|
403
|
+
display: inline-flex;
|
|
213
404
|
align-items: center;
|
|
214
|
-
gap:
|
|
215
|
-
|
|
405
|
+
gap: 6px;
|
|
406
|
+
cursor: pointer;
|
|
407
|
+
font-size: var(--text-sm);
|
|
408
|
+
color: var(--fg-2);
|
|
216
409
|
}
|
|
217
|
-
.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
410
|
+
details.more > summary::-webkit-details-marker { display: none; }
|
|
411
|
+
details.more > summary::before {
|
|
412
|
+
content: "";
|
|
413
|
+
width: 6px;
|
|
414
|
+
height: 6px;
|
|
415
|
+
border-right: 1.5px solid currentColor;
|
|
416
|
+
border-bottom: 1.5px solid currentColor;
|
|
417
|
+
transform: rotate(-45deg);
|
|
418
|
+
transition: transform 120ms ease;
|
|
222
419
|
}
|
|
420
|
+
details.more[open] > summary::before { transform: rotate(45deg); }
|
|
421
|
+
details.more[open] > summary { margin-bottom: 18px; }
|
|
422
|
+
details.more > div { display: grid; gap: 18px; }
|
|
423
|
+
|
|
424
|
+
.foot {
|
|
425
|
+
display: flex;
|
|
426
|
+
align-items: center;
|
|
427
|
+
gap: 10px;
|
|
428
|
+
padding: 14px 18px 18px;
|
|
429
|
+
}
|
|
430
|
+
:root[data-display-mode="fullscreen"] .foot { padding: 14px 20px; border-top: 1px solid var(--line); }
|
|
431
|
+
.price { flex: 1; min-width: 0; display: grid; gap: 1px; }
|
|
432
|
+
.price strong { font-size: var(--text-md); font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
433
|
+
.price span { font-size: var(--text-xs); color: var(--fg-2); }
|
|
223
434
|
button.action {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
border-radius: var(--border-radius-full, 999px);
|
|
228
|
-
padding: 8px 14px;
|
|
435
|
+
min-height: 44px;
|
|
436
|
+
border-radius: var(--pill);
|
|
437
|
+
padding: 0 20px;
|
|
229
438
|
cursor: pointer;
|
|
230
|
-
border: 1px solid var(--
|
|
439
|
+
border: 1px solid var(--line-2);
|
|
231
440
|
background: transparent;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
border-color: transparent;
|
|
236
|
-
background: var(--color-text-primary, #1b1b1a);
|
|
237
|
-
color: var(--color-background-primary, #ffffff);
|
|
441
|
+
font-size: var(--text-sm);
|
|
442
|
+
font-weight: 600;
|
|
443
|
+
transition: background 120ms ease;
|
|
238
444
|
}
|
|
445
|
+
button.action:hover:not(:disabled) { background: var(--bg-2); }
|
|
446
|
+
button.action.primary { border-color: transparent; background: var(--fg); color: var(--bg); }
|
|
447
|
+
button.action.primary:hover:not(:disabled) { background: var(--fg); opacity: 0.88; }
|
|
239
448
|
button.action:disabled { opacity: 0.5; cursor: default; }
|
|
449
|
+
:root:not([data-display-mode="fullscreen"]) .foot button.action { flex: 1; }
|
|
450
|
+
:root[data-display-mode="fullscreen"] #order { flex: 1; max-width: 200px; }
|
|
240
451
|
|
|
452
|
+
/* What the card is doing, as a small chip on the render. */
|
|
241
453
|
.status {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
454
|
+
position: absolute;
|
|
455
|
+
top: 12px;
|
|
456
|
+
left: 12px;
|
|
457
|
+
max-width: calc(100% - 24px);
|
|
458
|
+
box-sizing: border-box;
|
|
459
|
+
padding: 6px 12px;
|
|
460
|
+
border-radius: var(--pill);
|
|
461
|
+
background: color-mix(in srgb, var(--bg) 82%, transparent);
|
|
462
|
+
-webkit-backdrop-filter: blur(8px);
|
|
463
|
+
backdrop-filter: blur(8px);
|
|
464
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 0 0 1px var(--line);
|
|
465
|
+
font-size: var(--text-xs);
|
|
466
|
+
color: var(--fg);
|
|
245
467
|
}
|
|
246
468
|
.status:empty { display: none; }
|
|
469
|
+
|
|
470
|
+
/* Until the design arrives there is nothing to pick from: hold the controls back. */
|
|
471
|
+
.card:not([data-ready]) .needs-design { visibility: hidden; }
|
|
472
|
+
.card:not([data-ready]) button.action { opacity: 0.5; pointer-events: none; }
|
|
247
473
|
</style>
|
|
248
474
|
</head>
|
|
249
475
|
<body>
|
|
250
476
|
<div class="card" id="card">
|
|
477
|
+
<div class="top full-only">
|
|
478
|
+
<button class="icon-button close" type="button" id="close" aria-label="Close the editor">
|
|
479
|
+
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>
|
|
480
|
+
</button>
|
|
481
|
+
<strong>Pocket camera</strong>
|
|
482
|
+
<span class="muted">Changes save as you go</span>
|
|
483
|
+
</div>
|
|
484
|
+
|
|
251
485
|
<div class="stage">
|
|
252
486
|
<iframe id="viewer" title="3D preview of your camera" allow="fullscreen" hidden></iframe>
|
|
253
|
-
<div class="
|
|
487
|
+
<div class="preview" id="fallback" hidden></div>
|
|
488
|
+
<div class="status" id="status" role="status">Connecting…</div>
|
|
254
489
|
</div>
|
|
255
490
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
<div
|
|
261
|
-
<
|
|
262
|
-
<
|
|
491
|
+
<!-- Inline this is the card; fullscreen, the editor's panel. -->
|
|
492
|
+
<div class="panel" id="view-design">
|
|
493
|
+
<div class="scroll">
|
|
494
|
+
<div class="title inline-only">
|
|
495
|
+
<div>
|
|
496
|
+
<h2>Pocket camera</h2>
|
|
497
|
+
<p class="muted">A working camera in a shell you design</p>
|
|
263
498
|
</div>
|
|
264
|
-
<
|
|
499
|
+
<span class="price-tag needs-design" id="price-inline"></span>
|
|
265
500
|
</div>
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
<
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
501
|
+
|
|
502
|
+
<div class="block needs-design">
|
|
503
|
+
<h3 class="full-only">Colour</h3>
|
|
504
|
+
<div class="parts" role="tablist" aria-label="Part to colour">
|
|
505
|
+
<button type="button" role="tab" id="tab-shell" aria-selected="true" aria-controls="group-shell">
|
|
506
|
+
<span class="dot" id="dot-shell"></span>Body
|
|
507
|
+
</button>
|
|
508
|
+
<button type="button" role="tab" id="tab-button" aria-selected="false" aria-controls="group-button">
|
|
509
|
+
<span class="dot" id="dot-button"></span>Shutter
|
|
510
|
+
</button>
|
|
511
|
+
</div>
|
|
512
|
+
|
|
513
|
+
<div class="group" id="group-shell" role="tabpanel" aria-labelledby="tab-shell">
|
|
514
|
+
<div class="group-head">
|
|
515
|
+
<span>Body · <b id="note-shell"></b></span>
|
|
516
|
+
<div class="segmented small full-only" role="group" aria-label="Body finish">
|
|
517
|
+
<button type="button" id="solid-shell" aria-pressed="true">Solid</button>
|
|
518
|
+
<button type="button" id="gradient-shell" aria-pressed="false">Gradient</button>
|
|
519
|
+
</div>
|
|
520
|
+
</div>
|
|
521
|
+
<div class="stops full-only" id="stops-shell" hidden></div>
|
|
522
|
+
<div class="swatches" id="sw-shell"></div>
|
|
523
|
+
</div>
|
|
524
|
+
|
|
525
|
+
<div class="group" id="group-button" role="tabpanel" aria-labelledby="tab-button" hidden>
|
|
526
|
+
<div class="group-head">
|
|
527
|
+
<span>Shutter button · <b id="note-button"></b></span>
|
|
528
|
+
<div class="segmented small full-only" role="group" aria-label="Shutter button finish">
|
|
529
|
+
<button type="button" id="solid-button" aria-pressed="true">Solid</button>
|
|
530
|
+
<button type="button" id="gradient-button" aria-pressed="false">Gradient</button>
|
|
531
|
+
</div>
|
|
532
|
+
</div>
|
|
533
|
+
<div class="stops full-only" id="stops-button" hidden></div>
|
|
534
|
+
<div class="swatches" id="sw-button"></div>
|
|
279
535
|
</div>
|
|
280
|
-
<div class="stops" id="stops-button" hidden></div>
|
|
281
|
-
</div>
|
|
282
|
-
<div class="row">
|
|
283
|
-
<span></span>
|
|
284
|
-
<div class="swatches" id="sw-button"></div>
|
|
285
|
-
<label class="pick" title="Any colour"><input type="color" id="pick-button" aria-label="Button: any colour" /></label>
|
|
286
|
-
<button class="icon-button" type="button" id="dropper-button" title="Pick a colour from the screen" aria-label="Button: pick a colour from the screen" hidden>
|
|
287
|
-
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m2 22 1-1h3l9-9"/><path d="M3 21v-3l9-9"/><path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.4.4a2.1 2.1 0 1 1-3 3l-3.8-3.8a2.1 2.1 0 1 1 3-3l.4.4Z"/></svg>
|
|
288
|
-
</button>
|
|
289
536
|
</div>
|
|
537
|
+
|
|
538
|
+
<div class="full-only" id="knobs"></div>
|
|
290
539
|
</div>
|
|
291
540
|
<div class="foot">
|
|
292
|
-
<div class="price
|
|
293
|
-
|
|
541
|
+
<div class="price full-only">
|
|
542
|
+
<strong id="price"></strong>
|
|
543
|
+
<span>+ shipping</span>
|
|
544
|
+
</div>
|
|
545
|
+
<button class="action inline-only" id="customize" type="button">Customize</button>
|
|
294
546
|
<button class="action primary" id="order" type="button">Order</button>
|
|
295
547
|
</div>
|
|
296
548
|
</div>
|
|
297
|
-
|
|
298
|
-
<div class="status" id="status">Connecting…</div>
|
|
299
549
|
</div>
|
|
300
550
|
<script type="module" src="./main.ts"></script>
|
|
301
551
|
</body>
|