@pinepaper.studio/mcp-server 1.6.20 → 1.6.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +1 -1
- package/README.es.md +1 -1
- package/README.fr.md +1 -1
- package/README.hi.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +79 -5
- package/README.pt-BR.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/build-info.json +3 -3
- package/dist/ontology/upstream-vocabulary.d.ts +2 -2
- package/dist/ontology/upstream-vocabulary.js +3 -3
- package/dist/ontology/upstream-vocabulary.js.map +1 -1
- package/dist/ontology/vocabulary.js +1 -1
- package/dist/ontology/vocabulary.js.map +1 -1
- package/dist/tools/choreograph.d.ts.map +1 -1
- package/dist/tools/choreograph.js +10 -0
- package/dist/tools/choreograph.js.map +1 -1
- package/dist/tools/craft-tools.d.ts +55 -0
- package/dist/tools/craft-tools.d.ts.map +1 -0
- package/dist/tools/craft-tools.js +139 -0
- package/dist/tools/craft-tools.js.map +1 -0
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +117 -7
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/effect-class.d.ts.map +1 -1
- package/dist/tools/effect-class.js +6 -0
- package/dist/tools/effect-class.js.map +1 -1
- package/dist/tools/engine-lists.d.ts +2 -2
- package/dist/tools/engine-lists.js +2 -2
- package/dist/tools/engine-methods.d.ts.map +1 -1
- package/dist/tools/engine-methods.js +4 -0
- package/dist/tools/engine-methods.js.map +1 -1
- package/dist/tools/engine-surface.d.ts +1 -1
- package/dist/tools/engine-surface.d.ts.map +1 -1
- package/dist/tools/engine-surface.js +38 -5
- package/dist/tools/engine-surface.js.map +1 -1
- package/dist/tools/handlers/generate.d.ts +28 -28
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +125 -1
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts.map +1 -1
- package/dist/tools/minimal-descriptions.js +7 -0
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/shape-params.d.ts +2 -2
- package/dist/tools/shape-params.d.ts.map +1 -1
- package/dist/tools/shape-params.js +3 -2
- package/dist/tools/shape-params.js.map +1 -1
- package/dist/tools/stick-vocabulary.d.ts +1 -1
- package/dist/tools/stick-vocabulary.js +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +9 -0
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +10 -1
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +605 -107
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +1745 -582
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +144 -4
- package/dist/types/schemas.js.map +1 -1
- package/dist/vendor/design/design-style.d.ts +2 -2
- package/dist/vendor/design/design-style.d.ts.map +1 -1
- package/dist/vendor/design/design-style.js +7 -2
- package/dist/vendor/design/design-style.js.map +1 -1
- package/dist/vendor/design/subdivision-tool-schema.d.ts +16 -0
- package/dist/vendor/design/subdivision-tool-schema.d.ts.map +1 -0
- package/dist/vendor/design/subdivision-tool-schema.js +104 -0
- package/dist/vendor/design/subdivision-tool-schema.js.map +1 -0
- package/dist/vendor/design/textile-tool-schema.d.ts +14 -0
- package/dist/vendor/design/textile-tool-schema.d.ts.map +1 -0
- package/dist/vendor/design/textile-tool-schema.js +195 -0
- package/dist/vendor/design/textile-tool-schema.js.map +1 -0
- package/dist/vendor/design/ui-kit-runtime.d.ts +49 -0
- package/dist/vendor/design/ui-kit-runtime.d.ts.map +1 -0
- package/dist/vendor/design/ui-kit-runtime.js +761 -0
- package/dist/vendor/design/ui-kit-runtime.js.map +1 -0
- package/dist/vendor/design/ui-kit-tool-schema.d.ts +280 -0
- package/dist/vendor/design/ui-kit-tool-schema.d.ts.map +1 -0
- package/dist/vendor/design/ui-kit-tool-schema.js +209 -0
- package/dist/vendor/design/ui-kit-tool-schema.js.map +1 -0
- package/manifest.json +29 -1
- package/package.json +2 -2
- package/dist/assets/repositories/openclipart.d.ts +0 -41
- package/dist/assets/repositories/openclipart.d.ts.map +0 -1
- package/dist/assets/repositories/openclipart.js +0 -139
- package/dist/assets/repositories/openclipart.js.map +0 -1
- package/dist/assets/repositories/svgrepo.d.ts +0 -61
- package/dist/assets/repositories/svgrepo.d.ts.map +0 -1
- package/dist/assets/repositories/svgrepo.js +0 -276
- package/dist/assets/repositories/svgrepo.js.map +0 -1
|
@@ -0,0 +1,761 @@
|
|
|
1
|
+
/* GENERATED — DO NOT EDIT.
|
|
2
|
+
*
|
|
3
|
+
* Source: mcp-cloud/src/services/ui-kit-runtime.ts
|
|
4
|
+
* sha256: b3b1b6320a9d131077bcdf4b7f56d5e52ba0dbdb269348db419130200c16da47
|
|
5
|
+
* Generator: scripts/sync-design-systems.mjs
|
|
6
|
+
*
|
|
7
|
+
* Edit the TypeScript upstream and re-run the generator. A hand edit here is
|
|
8
|
+
* the second copy of the same design vocabulary that the generator exists to
|
|
9
|
+
* prevent, and the provenance test will fail on it.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* THE UI KIT — phone lock screens, notifications, prompt boxes, toggles, calendar and ad cards (D83, #81).
|
|
13
|
+
*
|
|
14
|
+
* A product video is mostly interface: a phone wakes, three notifications stack in, a prompt box types a
|
|
15
|
+
* request. Asked for that, a model stacked rectangles and guessed at what a Dynamic Island looks like, and
|
|
16
|
+
* every answer was a different, slightly wrong phone. The kit draws them once, properly, as parametric
|
|
17
|
+
* components the designer calls by name — `UIKit.phone(...)`, `UIKit.notifications(...)`,
|
|
18
|
+
* `UIKit.promptBox(...)` — and the result takes its colours, fonts and corner rounding from the brand kit
|
|
19
|
+
* when one is set.
|
|
20
|
+
*
|
|
21
|
+
* IT TRAVELS THE WAY THE RIG LIBRARY DOES (a863f78c): the source is served at /v1/ui-kit-runtime.js, the
|
|
22
|
+
* chat attaches it to generated code that calls the kit without defining it, and the render door attaches
|
|
23
|
+
* it to a scene's custom code (ui-kit-attach.ts). It never travels in the prompt: a model asked to paste a
|
|
24
|
+
* library paraphrases it and the paraphrase drops lines.
|
|
25
|
+
*
|
|
26
|
+
* BUILT ONLY FROM PRIMITIVES EVERY ENGINE DOOR DRAWS: `PinePaper.create` with rectangle (radius), circle,
|
|
27
|
+
* text (justification left/center/right) and path, animated only by `PinePaper.addAnimation` keyframes on
|
|
28
|
+
* opacity, y, x, scale and content. Nothing here reaches for a DOM, a font loader or a network. Each
|
|
29
|
+
* component is a set of ordinary registered items, so it can be selected, edited and recoloured afterwards.
|
|
30
|
+
*
|
|
31
|
+
* WHY A STRING: this is JavaScript that runs inside the engine (the Studio's canvas, the render container,
|
|
32
|
+
* the still engine), not code the Worker executes. Plain ES5 on purpose — no template literals, no arrow
|
|
33
|
+
* functions — so it parses in every one of those realms, and so this file can hold it in a String.raw.
|
|
34
|
+
*/
|
|
35
|
+
/** Names whose presence in generated code means the kit must be attached. */
|
|
36
|
+
export const UI_KIT_SYMBOLS = ["UIKit."];
|
|
37
|
+
/** The sentinel the runtime defines; code that carries it already has the kit. */
|
|
38
|
+
export const UI_KIT_SENTINEL = "__PP_UI_KIT__";
|
|
39
|
+
export const UI_KIT_RUNTIME = String.raw `// --- PINEPAPER UI KIT (attached automatically; do not paste) -------------------
|
|
40
|
+
var __PP_UI_KIT__ = true;
|
|
41
|
+
var UIKit = (function () {
|
|
42
|
+
var P = (typeof PinePaper !== 'undefined' && PinePaper) ? PinePaper : (typeof app !== 'undefined' ? app : null);
|
|
43
|
+
var PJ = (typeof paper !== 'undefined' && paper) ? paper : null;
|
|
44
|
+
var DEFAULT = {
|
|
45
|
+
name: '',
|
|
46
|
+
colors: { primary: '#0a84ff', secondary: '#5e5ce6', accent: '#34c759', background: '#f2f2f7', text: '#1c1c1e' },
|
|
47
|
+
fonts: { heading: 'Geist', body: 'Geist', mono: 'Geist Mono' },
|
|
48
|
+
radius: 1
|
|
49
|
+
};
|
|
50
|
+
var brand = null; // the kit set by UIKit.brand(), or captured from applyBrandKit
|
|
51
|
+
var kitEnd = 0; // the last keyframe time any component asked for
|
|
52
|
+
var minDuration = 0; // UIKit.duration(D)
|
|
53
|
+
var HOLD = 1.5; // seconds the last state is held on screen
|
|
54
|
+
|
|
55
|
+
// ---------- colour --------------------------------------------------------
|
|
56
|
+
function isColour(c) { return typeof c === 'string' && /^(#[0-9a-f]{3,8}|rgba?\(|hsla?\()/i.test(c.trim()); }
|
|
57
|
+
function rgb(hex) {
|
|
58
|
+
var h = String(hex || '').trim().replace('#', '');
|
|
59
|
+
if (h.length === 3 || h.length === 4) h = h.charAt(0) + h.charAt(0) + h.charAt(1) + h.charAt(1) + h.charAt(2) + h.charAt(2);
|
|
60
|
+
var n = parseInt(h.slice(0, 6), 16);
|
|
61
|
+
if (!isFinite(n)) return [128, 128, 128];
|
|
62
|
+
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
63
|
+
}
|
|
64
|
+
function hex(c) {
|
|
65
|
+
function p(v) { v = Math.max(0, Math.min(255, Math.round(v))); return (v < 16 ? '0' : '') + v.toString(16); }
|
|
66
|
+
return '#' + p(c[0]) + p(c[1]) + p(c[2]);
|
|
67
|
+
}
|
|
68
|
+
function mix(a, b, t) { var x = rgb(a), y = rgb(b); return hex([x[0] + (y[0] - x[0]) * t, x[1] + (y[1] - x[1]) * t, x[2] + (y[2] - x[2]) * t]); }
|
|
69
|
+
function lum(c) {
|
|
70
|
+
var v = rgb(c).map(function (u) { u /= 255; return u <= 0.03928 ? u / 12.92 : Math.pow((u + 0.055) / 1.055, 2.4); });
|
|
71
|
+
return 0.2126 * v[0] + 0.7152 * v[1] + 0.0722 * v[2];
|
|
72
|
+
}
|
|
73
|
+
function contrast(a, b) { var x = lum(a), y = lum(b); return (Math.max(x, y) + 0.05) / (Math.min(x, y) + 0.05); }
|
|
74
|
+
/** White or near-black, whichever reads on this colour. */
|
|
75
|
+
function ink(on) { return contrast('#ffffff', on) >= contrast('#111111', on) ? '#ffffff' : '#111111'; }
|
|
76
|
+
|
|
77
|
+
// ---------- theme -----------------------------------------------------------
|
|
78
|
+
function theme() {
|
|
79
|
+
var k = brand || {};
|
|
80
|
+
var c = k.colors || {};
|
|
81
|
+
var f = k.fonts || {};
|
|
82
|
+
var primary = isColour(c.primary) ? c.primary : DEFAULT.colors.primary;
|
|
83
|
+
var secondary = isColour(c.secondary) ? c.secondary : (brand ? primary : DEFAULT.colors.secondary);
|
|
84
|
+
var accent = isColour(c.accent) ? c.accent : (brand ? secondary : DEFAULT.colors.accent);
|
|
85
|
+
var radius = k.radius;
|
|
86
|
+
if (radius === 'sharp') radius = 0.35; else if (radius === 'soft') radius = 1; else if (radius === 'round') radius = 1.35;
|
|
87
|
+
radius = (typeof radius === 'number' && isFinite(radius)) ? Math.max(0, Math.min(2, radius)) : 1;
|
|
88
|
+
return {
|
|
89
|
+
branded: !!brand,
|
|
90
|
+
name: k.name || '',
|
|
91
|
+
primary: primary, secondary: secondary, accent: accent,
|
|
92
|
+
background: isColour(c.background) ? c.background : DEFAULT.colors.background,
|
|
93
|
+
text: isColour(c.text) ? c.text : DEFAULT.colors.text,
|
|
94
|
+
heading: f.heading || DEFAULT.fonts.heading,
|
|
95
|
+
body: f.body || f.heading || DEFAULT.fonts.body,
|
|
96
|
+
mono: f.mono || DEFAULT.fonts.mono,
|
|
97
|
+
radius: radius
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ---------- canvas & timeline ----------------------------------------------
|
|
102
|
+
/**
|
|
103
|
+
* The frame the render draws. PinePaper.canvas is the Studio shell's property and a render engine may
|
|
104
|
+
* not define it (the browser engine did not: a 1080x1920 phone was sized and centred for 1280x720,
|
|
105
|
+
* #81), so the engine's own artboard and then paper's view are asked before the 1280x720 guess.
|
|
106
|
+
*/
|
|
107
|
+
function canvas() {
|
|
108
|
+
var w = 0, h = 0;
|
|
109
|
+
function take(c) { if (!w && c && +c.width > 0 && +c.height > 0) { w = +c.width; h = +c.height; } }
|
|
110
|
+
try { if (P) take(P.canvas); } catch (e) {}
|
|
111
|
+
try { if (P && typeof P.getCanvasSize === 'function') take(P.getCanvasSize()); } catch (e) {}
|
|
112
|
+
try { if (P) take(P.canvasSize); } catch (e) {}
|
|
113
|
+
try { if (PJ && PJ.view) take(PJ.view.bounds); } catch (e) {}
|
|
114
|
+
return { width: w || 1280, height: h || 720 };
|
|
115
|
+
}
|
|
116
|
+
/** The canvas background, when the engine says what it is: text placed straight on the canvas reads against it. */
|
|
117
|
+
function ground() {
|
|
118
|
+
try { var b = P && (P.backgroundColor || P.background); if (isColour(b)) return b; } catch (e) {}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
function note(t) { if (t > kitEnd) kitEnd = t; }
|
|
122
|
+
function play() {
|
|
123
|
+
var D = Math.max(minDuration, kitEnd > 0 ? kitEnd + HOLD : 0);
|
|
124
|
+
if (D > 0 && P && P.playKeyframeTimeline) { try { P.playKeyframeTimeline(D, false); } catch (e) {} }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ---------- primitives --------------------------------------------------------
|
|
128
|
+
function made(list, id) { if (id !== undefined && id !== null) list.push({ id: id }); return id; }
|
|
129
|
+
function rect(list, x, y, w, h, r, fill, extra) {
|
|
130
|
+
var o = { x: x, y: y, width: Math.max(0.5, w), height: Math.max(0.5, h), radius: Math.max(0, Math.min(r, Math.min(w, h) / 2)) };
|
|
131
|
+
if (fill === null) o.fillColor = null; else o.color = fill;
|
|
132
|
+
if (extra) for (var k in extra) o[k] = extra[k];
|
|
133
|
+
var id = made(list, P.create('rectangle', o));
|
|
134
|
+
list[list.length - 1].cy = y;
|
|
135
|
+
return id;
|
|
136
|
+
}
|
|
137
|
+
function circle(list, x, y, r, fill, extra) {
|
|
138
|
+
var o = { x: x, y: y, radius: r, color: fill };
|
|
139
|
+
if (extra) for (var k in extra) o[k] = extra[k];
|
|
140
|
+
var id = made(list, P.create('circle', o));
|
|
141
|
+
list[list.length - 1].cy = y;
|
|
142
|
+
return id;
|
|
143
|
+
}
|
|
144
|
+
/** pathData authored around the origin with a bounding box centred on it, so x/y IS the centre. */
|
|
145
|
+
function path(list, x, y, d, fill, extra) {
|
|
146
|
+
var o = { x: x, y: y, pathData: d, closed: true, fillColor: fill, color: fill };
|
|
147
|
+
if (extra) for (var k in extra) o[k] = extra[k];
|
|
148
|
+
var id = made(list, P.create('path', o));
|
|
149
|
+
list[list.length - 1].cy = y;
|
|
150
|
+
return id;
|
|
151
|
+
}
|
|
152
|
+
/** Text: x is the left edge for align 'left', the right edge for 'right', the centre otherwise; y is the centre. */
|
|
153
|
+
function text(list, content, x, y, size, fill, weight, align, family, extra) {
|
|
154
|
+
var o = { x: x, y: y, content: String(content), fontSize: size, color: fill, fontFamily: family, fontWeight: String(weight || 400), justification: align || 'center' };
|
|
155
|
+
if (extra) for (var k in extra) o[k] = extra[k];
|
|
156
|
+
var id = made(list, P.create('text', o));
|
|
157
|
+
list[list.length - 1].cy = y;
|
|
158
|
+
return id;
|
|
159
|
+
}
|
|
160
|
+
var measured = {};
|
|
161
|
+
function measure(str, size, weight, family) {
|
|
162
|
+
str = String(str);
|
|
163
|
+
var key = size + '|' + weight + '|' + family + '|' + str;
|
|
164
|
+
if (measured[key] !== undefined) return measured[key];
|
|
165
|
+
return (measured[key] = measureNow(str, size, weight, family));
|
|
166
|
+
}
|
|
167
|
+
function measureNow(str, size, weight, family) {
|
|
168
|
+
if (PJ && PJ.PointText) {
|
|
169
|
+
try {
|
|
170
|
+
var t = new PJ.PointText({ content: str, fontSize: size, fontWeight: String(weight || 400), fontFamily: family });
|
|
171
|
+
var w = t.bounds.width; t.remove();
|
|
172
|
+
if (w > 0 && isFinite(w)) return w;
|
|
173
|
+
} catch (e) {}
|
|
174
|
+
}
|
|
175
|
+
return str.length * size * 0.54;
|
|
176
|
+
}
|
|
177
|
+
/** Greedy word wrap into at most maxLines lines, the last one ellipsised if it had to be cut. */
|
|
178
|
+
function wrap(str, maxWidth, size, weight, family, maxLines) {
|
|
179
|
+
var words = String(str || '').split(/\s+/).filter(Boolean), lines = [], cur = '';
|
|
180
|
+
for (var i = 0; i < words.length; i++) {
|
|
181
|
+
var trial = cur ? cur + ' ' + words[i] : words[i];
|
|
182
|
+
if (measure(trial, size, weight, family) <= maxWidth || !cur) cur = trial;
|
|
183
|
+
else { lines.push(cur); cur = words[i]; if (lines.length === maxLines) { cur = ''; lines[maxLines - 1] += ' ' + words.slice(i).join(' '); break; } }
|
|
184
|
+
}
|
|
185
|
+
if (cur) lines.push(cur);
|
|
186
|
+
if (lines.length > maxLines) lines = lines.slice(0, maxLines);
|
|
187
|
+
for (var j = 0; j < lines.length; j++) lines[j] = fit(lines[j], maxWidth, size, weight, family);
|
|
188
|
+
return lines;
|
|
189
|
+
}
|
|
190
|
+
function fit(str, maxWidth, size, weight, family) {
|
|
191
|
+
str = String(str);
|
|
192
|
+
if (measure(str, size, weight, family) <= maxWidth) return str;
|
|
193
|
+
while (str.length > 1 && measure(str + '…', size, weight, family) > maxWidth) str = str.slice(0, -1);
|
|
194
|
+
return str.replace(/\s+$/, '') + '…';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ---------- motion ------------------------------------------------------------
|
|
198
|
+
/**
|
|
199
|
+
* One addAnimation per item, carrying every animated property in every key: a second track on the
|
|
200
|
+
* same item fights the first. keys: [{ t, opacity, dy, scale }] — dy is relative to the item's own
|
|
201
|
+
* creation y, so a whole component moves together.
|
|
202
|
+
*/
|
|
203
|
+
function animate(list, keys, easing) {
|
|
204
|
+
if (!keys || !keys.length || !P || !P.addAnimation) return;
|
|
205
|
+
keys = keys.slice().sort(function (a, b) { return a.t - b.t; });
|
|
206
|
+
if (keys[0].t > 0) keys.unshift(copy(keys[0], 0));
|
|
207
|
+
// Every key carries every property that any key animates: a property missing from one key has no
|
|
208
|
+
// value to interpolate from. y is animated only when something actually moves, so a component
|
|
209
|
+
// that merely fades never has its items re-placed.
|
|
210
|
+
var moves = keys.some(function (k) { return k.dy; });
|
|
211
|
+
var props = ['opacity', 'dy', 'scale'];
|
|
212
|
+
props.forEach(function (p) {
|
|
213
|
+
var any = keys.some(function (k) { return k[p] !== undefined; });
|
|
214
|
+
if (!any) return;
|
|
215
|
+
var first; for (var a = 0; a < keys.length; a++) if (keys[a][p] !== undefined) { first = keys[a][p]; break; }
|
|
216
|
+
var cur = first;
|
|
217
|
+
keys = keys.map(function (k) { var c = copy(k, k.t); if (c[p] === undefined) c[p] = cur; else cur = c[p]; return c; });
|
|
218
|
+
});
|
|
219
|
+
if (!moves) keys = keys.map(function (k) { var c = copy(k, k.t); delete c.dy; return c; });
|
|
220
|
+
var last = keys[keys.length - 1].t;
|
|
221
|
+
for (var i = 0; i < list.length; i++) {
|
|
222
|
+
var it = list[i];
|
|
223
|
+
var frames = keys.map(function (k) {
|
|
224
|
+
var props = {};
|
|
225
|
+
if (k.opacity !== undefined) props.opacity = k.opacity * (it.alpha === undefined ? 1 : it.alpha);
|
|
226
|
+
if (k.dy !== undefined && it.cy !== undefined) props.y = it.cy + k.dy;
|
|
227
|
+
if (k.scale !== undefined) props.scale = k.scale;
|
|
228
|
+
return { time: k.t, properties: props, easing: k.easing || easing || 'easeOut' };
|
|
229
|
+
});
|
|
230
|
+
try { P.addAnimation(it.id, frames, { duration: Math.max(last, 0.01), loop: false }); } catch (e) {}
|
|
231
|
+
}
|
|
232
|
+
note(last);
|
|
233
|
+
}
|
|
234
|
+
function copy(k, t) { var o = {}; for (var p in k) o[p] = k[p]; o.t = t; return o; }
|
|
235
|
+
/** Opacity window: hidden until at, in over fade, out at until. */
|
|
236
|
+
function windowKeys(at, fade, until, rise) {
|
|
237
|
+
var keys = [];
|
|
238
|
+
at = +at || 0; fade = fade === undefined ? 0.4 : +fade; rise = +rise || 0;
|
|
239
|
+
if (at > 0 || rise) {
|
|
240
|
+
keys.push({ t: Math.max(0, at), opacity: 0, dy: rise });
|
|
241
|
+
keys.push({ t: Math.max(0, at) + fade, opacity: 1, dy: 0 });
|
|
242
|
+
}
|
|
243
|
+
if (until !== undefined && until !== null && isFinite(until)) {
|
|
244
|
+
if (!keys.length) keys.push({ t: 0, opacity: 1, dy: 0 });
|
|
245
|
+
keys.push({ t: +until, opacity: 1, dy: 0 });
|
|
246
|
+
keys.push({ t: +until + fade, opacity: 0, dy: 0 });
|
|
247
|
+
}
|
|
248
|
+
return keys;
|
|
249
|
+
}
|
|
250
|
+
function settle(list, o, rise) {
|
|
251
|
+
var keys = windowKeys(o.at, o.fade, o.until, rise);
|
|
252
|
+
if (keys.length) animate(list, keys);
|
|
253
|
+
play();
|
|
254
|
+
}
|
|
255
|
+
function ids(list) { return list.map(function (it) { return it.id; }); }
|
|
256
|
+
function glyphAlpha(list, a) { list[list.length - 1].alpha = a; }
|
|
257
|
+
|
|
258
|
+
// ---------- phone -------------------------------------------------------------
|
|
259
|
+
// Drawn in iPhone points (screen 390 x 844) and scaled: the phone is the unit every other component
|
|
260
|
+
// placed on it inherits through phone.s.
|
|
261
|
+
var FRAME_W = 414, FRAME_H = 868, BEZEL = 12, SCREEN_W = 390, SCREEN_H = 844;
|
|
262
|
+
|
|
263
|
+
function phone(o) {
|
|
264
|
+
o = o || {};
|
|
265
|
+
var th = theme(), cv = canvas();
|
|
266
|
+
var maxH = cv.height * (cv.height > cv.width ? 0.78 : 0.88);
|
|
267
|
+
var H = +o.height || Math.min(maxH, cv.width * 0.9 * FRAME_H / FRAME_W);
|
|
268
|
+
var s = H / FRAME_H, W = FRAME_W * s;
|
|
269
|
+
var cx = o.x === undefined ? cv.width / 2 : +o.x, cy = o.y === undefined ? cv.height / 2 : +o.y;
|
|
270
|
+
var dark = o.dark !== false;
|
|
271
|
+
var frameCol = isColour(o.frame) ? o.frame : '#1d1d1f';
|
|
272
|
+
var sw = SCREEN_W * s, sh = SCREEN_H * s, sl = cx - sw / 2, st = cy - sh / 2;
|
|
273
|
+
var wall = o.wallpaper;
|
|
274
|
+
var stops = Array.isArray(wall) ? wall.filter(isColour) : (isColour(wall) ? [wall] : [th.primary, mix(th.secondary, '#000000', 0.45)]);
|
|
275
|
+
if (!stops.length) stops = [th.primary, mix(th.secondary, '#000000', 0.45)];
|
|
276
|
+
var avg = stops.length > 1 ? mix(stops[0], stops[stops.length - 1], 0.5) : stops[0];
|
|
277
|
+
var onWall = isColour(o.ink) ? o.ink : ink(avg);
|
|
278
|
+
var list = [];
|
|
279
|
+
|
|
280
|
+
// body, buttons, glass
|
|
281
|
+
rect(list, cx + 6 * s, cy + 14 * s, W, FRAME_H * s, 66 * s, '#000000', { opacity: 0.28 }); glyphAlpha(list, 0.28);
|
|
282
|
+
var btn = '#2a2a2d';
|
|
283
|
+
rect(list, cx - W / 2 - 1.5 * s, st + 160 * s, 4 * s, 34 * s, 2 * s, btn);
|
|
284
|
+
rect(list, cx - W / 2 - 1.5 * s, st + 235 * s, 4 * s, 62 * s, 2 * s, btn);
|
|
285
|
+
rect(list, cx - W / 2 - 1.5 * s, st + 310 * s, 4 * s, 62 * s, 2 * s, btn);
|
|
286
|
+
rect(list, cx + W / 2 + 1.5 * s, st + 270 * s, 4 * s, 98 * s, 2 * s, btn);
|
|
287
|
+
rect(list, cx, cy, W, FRAME_H * s, 66 * s, frameCol, { strokeColor: mix(frameCol, '#ffffff', 0.22), strokeWidth: Math.max(1, 2 * s) });
|
|
288
|
+
var fill = stops.length > 1
|
|
289
|
+
? { gradient: { stops: stops }, origin: [sl, st], destination: [sl + sw * 0.6, st + sh] }
|
|
290
|
+
: stops[0];
|
|
291
|
+
var screenId = rect(list, cx, cy, sw, sh, 54 * s, fill);
|
|
292
|
+
|
|
293
|
+
// status bar + Dynamic Island
|
|
294
|
+
var sbY = st + 29 * s;
|
|
295
|
+
if (o.island !== false) rect(list, cx, sbY, 124 * s, 36 * s, 18 * s, '#000000');
|
|
296
|
+
if (o.statusBar !== false) {
|
|
297
|
+
text(list, o.time || '9:41', sl + 66 * s, sbY, 17 * s, onWall, 600, 'center', th.body);
|
|
298
|
+
var bx = sl + sw - 92 * s;
|
|
299
|
+
for (var i = 0; i < 4; i++) rect(list, bx + i * 5.2 * s, sbY + (4 - i) * 1.4 * s, 3.2 * s, (5 + i * 2.8) * s, 1 * s, onWall);
|
|
300
|
+
// wi-fi: three nested fans, drawn as one centred path
|
|
301
|
+
var r1 = 7.6 * s, r2 = 4.9 * s, r3 = 2.2 * s;
|
|
302
|
+
var fan = function (r, r0) {
|
|
303
|
+
var a0 = -Math.PI * 0.78, a1 = -Math.PI * 0.22, pts = [];
|
|
304
|
+
for (var k = 0; k <= 8; k++) { var a = a0 + (a1 - a0) * k / 8; pts.push([Math.cos(a) * r, Math.sin(a) * r + 3.6 * s]); }
|
|
305
|
+
for (var m = 8; m >= 0; m--) { var b = a0 + (a1 - a0) * m / 8; pts.push([Math.cos(b) * r0, Math.sin(b) * r0 + 3.6 * s]); }
|
|
306
|
+
return 'M ' + pts.map(function (p) { return p[0].toFixed(2) + ' ' + p[1].toFixed(2); }).join(' L ') + ' Z';
|
|
307
|
+
};
|
|
308
|
+
path(list, sl + sw - 64 * s, sbY - 1 * s, fan(r1, r1 - 1.9 * s) + ' ' + fan(r2, r2 - 1.9 * s) + ' ' + fan(r3, 0.01), onWall);
|
|
309
|
+
rect(list, sl + sw - 37 * s, sbY, 25 * s, 12 * s, 3.6 * s, null, { strokeColor: onWall, strokeWidth: Math.max(0.8, 1.1 * s), opacity: 0.45 }); glyphAlpha(list, 0.45);
|
|
310
|
+
rect(list, sl + sw - 38.2 * s, sbY, 19.6 * s, 8 * s, 2 * s, onWall);
|
|
311
|
+
rect(list, sl + sw - 22.6 * s, sbY, 1.6 * s, 4 * s, 0.8 * s, onWall, { opacity: 0.45 }); glyphAlpha(list, 0.45);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// lock screen
|
|
315
|
+
var lock = o.lockScreen !== false;
|
|
316
|
+
if (lock) {
|
|
317
|
+
text(list, o.date || 'Tuesday, June 9', cx, st + 118 * s, 21 * s, onWall, 600, 'center', th.body, { opacity: 0.88 }); glyphAlpha(list, 0.88);
|
|
318
|
+
text(list, o.clock || o.time || '9:41', cx, st + 196 * s, 98 * s, onWall, 700, 'center', th.heading);
|
|
319
|
+
var gl = onWall === '#ffffff' ? '#ffffff' : '#000000';
|
|
320
|
+
circle(list, sl + 66 * s, st + sh - 92 * s, 25 * s, gl, { opacity: 0.18 }); glyphAlpha(list, 0.18);
|
|
321
|
+
circle(list, sl + sw - 66 * s, st + sh - 92 * s, 25 * s, gl, { opacity: 0.18 }); glyphAlpha(list, 0.18);
|
|
322
|
+
// torch and camera glyphs, centred on the origin
|
|
323
|
+
path(list, sl + 66 * s, st + sh - 92 * s, 'M ' + (-4 * s) + ' ' + (-10 * s) + ' L ' + (4 * s) + ' ' + (-10 * s) + ' L ' + (4 * s) + ' ' + (-4 * s) + ' L ' + (2 * s) + ' ' + (0) + ' L ' + (2 * s) + ' ' + (10 * s) + ' L ' + (-2 * s) + ' ' + (10 * s) + ' L ' + (-2 * s) + ' ' + (0) + ' L ' + (-4 * s) + ' ' + (-4 * s) + ' Z', onWall);
|
|
324
|
+
rect(list, sl + sw - 66 * s, st + sh - 92 * s, 22 * s, 16 * s, 4 * s, onWall);
|
|
325
|
+
circle(list, sl + sw - 66 * s, st + sh - 92 * s, 4.6 * s, gl === '#ffffff' ? mix(avg, '#000000', 0.2) : '#ffffff');
|
|
326
|
+
}
|
|
327
|
+
if (o.homeIndicator !== false) rect(list, cx, st + sh - 9 * s, 136 * s, 5 * s, 2.5 * s, onWall);
|
|
328
|
+
|
|
329
|
+
settle(list, o);
|
|
330
|
+
var ph = {
|
|
331
|
+
kind: 'phone', s: s, x: cx, y: cy, width: W, height: FRAME_H * s, ink: onWall, dark: dark,
|
|
332
|
+
screen: { x: cx, y: cy, width: sw, height: sh, left: sl, top: st, right: sl + sw, bottom: st + sh, id: screenId },
|
|
333
|
+
/** Canvas position of a point given in phone-screen points (0..390, 0..844). */
|
|
334
|
+
at: function (px, py) { return { x: sl + px * s, y: st + py * s }; },
|
|
335
|
+
ids: ids(list)
|
|
336
|
+
};
|
|
337
|
+
return ph;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ---------- notifications -------------------------------------------------------
|
|
341
|
+
function cardTheme(o, th) {
|
|
342
|
+
var dark = !!o.dark;
|
|
343
|
+
return dark
|
|
344
|
+
? { bg: '#1c1c1e', alpha: 0.78, title: '#ffffff', body: '#ebebf5', meta: '#ebebf5', metaA: 0.6 }
|
|
345
|
+
: { bg: '#ffffff', alpha: 0.82, title: '#111111', body: '#1c1c1e', meta: '#3c3c43', metaA: 0.6 };
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Build one notification's items at slot-0 position and return them with their height. Not animated.
|
|
349
|
+
*/
|
|
350
|
+
function noteItems(n, i, x, top, width, s, th, ct) {
|
|
351
|
+
var list = [];
|
|
352
|
+
var pad = 14 * s, icon = 38 * s, fsT = 15 * s, fsB = 15 * s, fsM = 13 * s;
|
|
353
|
+
var textLeft = x - width / 2 + pad + icon + 12 * s, textW = width / 2 + x - pad - textLeft;
|
|
354
|
+
var title = n.title || n.app || 'Notification';
|
|
355
|
+
var lines = wrap(n.body || '', textW, fsB, 400, th.body, n.lines || 2);
|
|
356
|
+
var h = pad * 2 + fsT * 1.25 + lines.length * fsB * 1.28;
|
|
357
|
+
h = Math.max(h, icon + pad * 2);
|
|
358
|
+
var cy = top + h / 2;
|
|
359
|
+
var colours = [th.primary, th.accent, th.secondary];
|
|
360
|
+
var ic = isColour(n.color) ? n.color : colours[i % colours.length];
|
|
361
|
+
rect(list, x, cy, width, h, 24 * s * th.radius, ct.bg, { opacity: ct.alpha }); glyphAlpha(list, ct.alpha);
|
|
362
|
+
var ix = x - width / 2 + pad + icon / 2;
|
|
363
|
+
rect(list, ix, cy, icon, icon, 9.5 * s * th.radius, ic);
|
|
364
|
+
text(list, (n.icon || String(n.app || title).charAt(0) || '•').slice(0, 2), ix, cy, 19 * s, ink(ic), 700, 'center', th.heading);
|
|
365
|
+
var metaText = n.time || 'now';
|
|
366
|
+
var metaW = measure(metaText, fsM, 400, th.body);
|
|
367
|
+
var tY = top + pad + fsT * 0.62;
|
|
368
|
+
text(list, fit(title, textW - metaW - 10 * s, fsT, 600, th.body), textLeft, tY, fsT, ct.title, 600, 'left', th.body);
|
|
369
|
+
text(list, metaText, x + width / 2 - pad, tY, fsM, ct.meta, 400, 'right', th.body, { opacity: ct.metaA }); glyphAlpha(list, ct.metaA);
|
|
370
|
+
for (var l = 0; l < lines.length; l++) text(list, lines[l], textLeft, tY + fsT * 0.66 + fsB * 0.64 + l * fsB * 1.28, fsB, ct.body, 400, 'left', th.body);
|
|
371
|
+
return { list: list, h: h };
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* A stack of notifications arriving one by one. Newest on top pushes the others down (iOS), or
|
|
375
|
+
* newest: 'bottom' to append below. phoneOrOpts may be a phone from UIKit.phone() — the stack then
|
|
376
|
+
* sits under the clock and inherits its scale — or plain options { x, y(top), width, scale }.
|
|
377
|
+
*/
|
|
378
|
+
function notifications(phoneOrOpts, items, o) {
|
|
379
|
+
o = o || {};
|
|
380
|
+
// notifications(phone, items, opts) | notifications(items, opts) | notifications({ items, x, y, ... })
|
|
381
|
+
var th = theme(), ph = phoneOrOpts && phoneOrOpts.kind === 'phone' ? phoneOrOpts : null;
|
|
382
|
+
if (!ph && Array.isArray(phoneOrOpts)) { o = items || {}; items = phoneOrOpts; }
|
|
383
|
+
else if (!ph && phoneOrOpts && typeof phoneOrOpts === 'object') { o = phoneOrOpts; items = o.items; }
|
|
384
|
+
o = o || {};
|
|
385
|
+
if (!ph && o.phone && o.phone.kind === 'phone') ph = o.phone;
|
|
386
|
+
items = (items || []).map(function (n) { return typeof n === 'string' ? { title: n } : (n || {}); });
|
|
387
|
+
var cv = canvas();
|
|
388
|
+
var s = ph ? ph.s : (+o.scale || Math.min(cv.width, cv.height) / 720);
|
|
389
|
+
var width = +o.width || (ph ? ph.screen.width - 24 * s : 360 * s);
|
|
390
|
+
var x = o.x !== undefined ? +o.x : (ph ? ph.screen.x : cv.width / 2);
|
|
391
|
+
var top = o.y !== undefined ? +o.y : (ph ? ph.screen.top + 300 * s : cv.height * 0.25);
|
|
392
|
+
var gap = (o.gap === undefined ? 8 : +o.gap) * s;
|
|
393
|
+
var start = o.start === undefined ? 0.6 : +o.start, stagger = o.stagger === undefined ? 0.9 : +o.stagger;
|
|
394
|
+
var newestTop = o.newest !== 'bottom';
|
|
395
|
+
var ct = cardTheme(o, th);
|
|
396
|
+
var built = [];
|
|
397
|
+
for (var i = 0; i < items.length; i++) built.push(noteItems(items[i], i, x, top, width, s, th, ct));
|
|
398
|
+
var times = items.map(function (n, k) { return n.at !== undefined ? +n.at : start + k * stagger; });
|
|
399
|
+
var IN = 0.45, RISE = -16 * s;
|
|
400
|
+
var all = [];
|
|
401
|
+
for (var j = 0; j < built.length; j++) {
|
|
402
|
+
// y offset of card j once card k has arrived
|
|
403
|
+
var offset = function (k) {
|
|
404
|
+
if (!newestTop) { var d0 = 0; for (var m0 = 0; m0 < j; m0++) d0 += built[m0].h + gap; return d0; }
|
|
405
|
+
var d = 0; for (var m = j + 1; m <= k; m++) d += built[m].h + gap; return d;
|
|
406
|
+
};
|
|
407
|
+
var keys = [{ t: 0, opacity: 0, dy: offset(j) + RISE }, { t: times[j], opacity: 0, dy: offset(j) + RISE },
|
|
408
|
+
{ t: times[j] + IN, opacity: 1, dy: offset(j) }];
|
|
409
|
+
if (newestTop) for (var k = j + 1; k < built.length; k++) {
|
|
410
|
+
keys.push({ t: times[k], opacity: 1, dy: offset(k - 1) });
|
|
411
|
+
keys.push({ t: times[k] + IN, opacity: 1, dy: offset(k) });
|
|
412
|
+
}
|
|
413
|
+
if (o.until !== undefined) { keys.push({ t: +o.until, opacity: 1, dy: offset(built.length - 1) }); keys.push({ t: +o.until + 0.4, opacity: 0, dy: offset(built.length - 1) }); }
|
|
414
|
+
if (times[j] <= 0 && !newestTop && o.until === undefined) keys = [];
|
|
415
|
+
animate(built[j].list, keys);
|
|
416
|
+
all = all.concat(built[j].list);
|
|
417
|
+
}
|
|
418
|
+
play();
|
|
419
|
+
var total = 0; for (var q = 0; q < built.length; q++) total += built[q].h + (q ? gap : 0);
|
|
420
|
+
return { kind: 'notifications', ids: ids(all), cards: built.map(function (b) { return ids(b.list); }), arrivals: times,
|
|
421
|
+
top: top, bottom: top + total, height: total, doneAt: times.length ? times[times.length - 1] + IN : 0 };
|
|
422
|
+
}
|
|
423
|
+
function notification(o) {
|
|
424
|
+
o = o || {};
|
|
425
|
+
return notifications({ phone: o.phone, items: [o], x: o.x, y: o.y, width: o.width, scale: o.scale, dark: o.dark, start: o.at === undefined ? 0 : o.at, until: o.until });
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// ---------- prompt box ------------------------------------------------------------
|
|
429
|
+
/**
|
|
430
|
+
* A chat input that types its prompt. text types from typeAt at cps characters a second, a caret
|
|
431
|
+
* follows the last character, and the send button presses when typing finishes.
|
|
432
|
+
*/
|
|
433
|
+
function promptBox(o) {
|
|
434
|
+
o = o || {};
|
|
435
|
+
var th = theme(), cv = canvas(), ph = o.phone && o.phone.kind === 'phone' ? o.phone : null;
|
|
436
|
+
var s = ph ? ph.s : (+o.scale || Math.min(cv.width, cv.height) / 720);
|
|
437
|
+
var width = +o.width || (ph ? ph.screen.width - 32 * s : Math.min(cv.width * 0.6, 640 * s));
|
|
438
|
+
var height = +o.height || 56 * s;
|
|
439
|
+
var x = o.x !== undefined ? +o.x : (ph ? ph.screen.x : cv.width / 2);
|
|
440
|
+
var y = o.y !== undefined ? +o.y : (ph ? ph.screen.bottom - 172 * s : cv.height * 0.6);
|
|
441
|
+
var dark = !!o.dark;
|
|
442
|
+
var bg = dark ? '#1c1c1e' : '#ffffff', stroke = dark ? '#3a3a3c' : '#d1d1d6';
|
|
443
|
+
var fg = dark ? '#ffffff' : (contrast(th.text, '#ffffff') >= 4.5 ? th.text : '#111111');
|
|
444
|
+
var fs = +o.fontSize || Math.round(height * 0.31 * 10) / 10;
|
|
445
|
+
var r = Math.min(height / 2, height / 2 * th.radius);
|
|
446
|
+
var list = [];
|
|
447
|
+
if (o.glow !== false) { rect(list, x, y, width + 10 * s, height + 10 * s, r + 5 * s, th.primary, { opacity: 0.16 }); glyphAlpha(list, 0.16); }
|
|
448
|
+
rect(list, x, y + 3 * s, width, height, r, '#000000', { opacity: dark ? 0.35 : 0.08 }); glyphAlpha(list, dark ? 0.35 : 0.08);
|
|
449
|
+
rect(list, x, y, width, height, r, bg, { strokeColor: stroke, strokeWidth: Math.max(1, 1.2 * s) });
|
|
450
|
+
var left = x - width / 2 + height * 0.36;
|
|
451
|
+
if (o.attach !== false) {
|
|
452
|
+
var pr = height * 0.2;
|
|
453
|
+
path(list, left + pr, y, 'M ' + (-pr) + ' ' + (-0.9 * s) + ' L ' + pr + ' ' + (-0.9 * s) + ' L ' + pr + ' ' + (0.9 * s) + ' L ' + (-pr) + ' ' + (0.9 * s) + ' Z M ' + (-0.9 * s) + ' ' + (-pr) + ' L ' + (0.9 * s) + ' ' + (-pr) + ' L ' + (0.9 * s) + ' ' + pr + ' L ' + (-0.9 * s) + ' ' + pr + ' Z', fg, { opacity: 0.55 }); glyphAlpha(list, 0.55);
|
|
454
|
+
left += pr * 2 + height * 0.24;
|
|
455
|
+
}
|
|
456
|
+
var bR = height * 0.36, bX = x + width / 2 - height * 0.14 - bR;
|
|
457
|
+
var textW = bX - bR - 10 * s - left;
|
|
458
|
+
// The whole prompt is typed. A line longer than the field scrolls, as a real input does: the field
|
|
459
|
+
// shows the END of what has been typed behind a leading ellipsis, so no word of the prompt is lost.
|
|
460
|
+
var full = String(o.text || '').slice(0, 400);
|
|
461
|
+
var shown = function (str) {
|
|
462
|
+
if (measure(str, fs, 400, th.body) <= textW) return str;
|
|
463
|
+
var t = str;
|
|
464
|
+
while (t.length > 1 && measure('\u2026' + t, fs, 400, th.body) > textW) t = t.slice(1);
|
|
465
|
+
return '\u2026' + t;
|
|
466
|
+
};
|
|
467
|
+
var placeholder = fit(o.placeholder || 'Ask anything', textW, fs, 400, th.body);
|
|
468
|
+
var typeAt = o.typeAt === undefined ? (+o.at || 0) + 0.8 : +o.typeAt;
|
|
469
|
+
var cps = +o.cps || 22;
|
|
470
|
+
var doneAt = typeAt + full.length / cps;
|
|
471
|
+
var baseList = list;
|
|
472
|
+
var phList = [];
|
|
473
|
+
text(phList, placeholder, left, y, fs, fg, 400, 'left', th.body, { opacity: 0.4 }); glyphAlpha(phList, 0.4);
|
|
474
|
+
var typed = [];
|
|
475
|
+
var typedId = null;
|
|
476
|
+
var caretH = fs * 1.25;
|
|
477
|
+
var caret = [];
|
|
478
|
+
var caretId = rect(caret, left + 1 * s, y, Math.max(1.5, 2 * s), caretH, 1 * s, th.primary);
|
|
479
|
+
var btn = [];
|
|
480
|
+
circle(btn, bX, y, bR, th.primary);
|
|
481
|
+
var a = bR * 0.5, sh = Math.max(1.2, 1.9 * s);
|
|
482
|
+
path(btn, bX, y, 'M 0 ' + (-a) + ' L ' + (a * 0.85) + ' ' + (-a * 0.15) + ' L ' + (a * 0.85 - sh) + ' ' + (-a * 0.15 + sh) + ' L ' + (sh / 2) + ' ' + (-a + sh * 1.6) + ' L ' + (sh / 2) + ' ' + a + ' L ' + (-sh / 2) + ' ' + a + ' L ' + (-sh / 2) + ' ' + (-a + sh * 1.6) + ' L ' + (-a * 0.85 + sh) + ' ' + (-a * 0.15 + sh) + ' L ' + (-a * 0.85) + ' ' + (-a * 0.15) + ' Z', ink(th.primary));
|
|
483
|
+
|
|
484
|
+
// TYPING IS A RUN OF PREFIX LINES, each shown for its own instant. One text item stepping its
|
|
485
|
+
// its content was tried first and measured wrong: the engine re-places a text item after a content
|
|
486
|
+
// change (it keeps a captured position, not the left edge), so the line slid left out of the box, and
|
|
487
|
+
// keyframing x did not pin it either. A text created at x with justification 'left' DOES start at x
|
|
488
|
+
// (measured), and a prefix that never changes never moves. Long prompts step two or more characters
|
|
489
|
+
// at a time so a sentence stays a few dozen items.
|
|
490
|
+
var stepN = Math.max(1, Math.ceil(full.length / 48));
|
|
491
|
+
var cuts = [];
|
|
492
|
+
for (var c = stepN; c < full.length; c += stepN) cuts.push(c);
|
|
493
|
+
if (full.length) cuts.push(full.length);
|
|
494
|
+
var EPS = 0.001;
|
|
495
|
+
var endT = o.until !== undefined ? +o.until : null;
|
|
496
|
+
for (var q = 0; q < cuts.length; q++) {
|
|
497
|
+
var one = [];
|
|
498
|
+
typedId = text(one, shown(full.slice(0, cuts[q])), left, y, fs, fg, 400, 'left', th.body);
|
|
499
|
+
typed.push(one[0]);
|
|
500
|
+
var tOn = typeAt + cuts[q] / cps, tOff = q + 1 < cuts.length ? typeAt + cuts[q + 1] / cps : endT;
|
|
501
|
+
var kf = [{ time: 0, properties: { opacity: 0 }, easing: 'linear' }, { time: Math.max(0, tOn - EPS), properties: { opacity: 0 }, easing: 'linear' }, { time: tOn, properties: { opacity: 1 }, easing: 'linear' }];
|
|
502
|
+
if (tOff !== null) {
|
|
503
|
+
var fadeOut = q + 1 < cuts.length ? EPS : 0.4;
|
|
504
|
+
kf.push({ time: tOff, properties: { opacity: 1 }, easing: 'linear' }, { time: tOff + fadeOut, properties: { opacity: 0 }, easing: 'linear' });
|
|
505
|
+
}
|
|
506
|
+
try { P.addAnimation(typedId, kf, { duration: Math.max(kf[kf.length - 1].time, 0.01), loop: false }); } catch (e) {}
|
|
507
|
+
note(kf[kf.length - 1].time);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// caret: follows the measured end of each prefix, then blinks
|
|
511
|
+
var step = Math.max(1, Math.ceil(full.length / 40));
|
|
512
|
+
var ck = [{ time: 0, properties: { x: left + 1 * s, opacity: o.at ? 0 : 1 } }];
|
|
513
|
+
if (o.at) ck.push({ time: +o.at + 0.4, properties: { x: left + 1 * s, opacity: 1 } });
|
|
514
|
+
if (typeAt > 0) ck.push({ time: typeAt, properties: { x: left + 1 * s, opacity: 1 } });
|
|
515
|
+
for (var c2 = step; c2 <= full.length; c2 += step) ck.push({ time: typeAt + c2 / cps, properties: { x: left + measure(shown(full.slice(0, c2)), fs, 400, th.body) + 2.5 * s, opacity: 1 } });
|
|
516
|
+
var endX = left + measure(shown(full), fs, 400, th.body) + 2.5 * s;
|
|
517
|
+
ck.push({ time: doneAt, properties: { x: endX, opacity: 1 } });
|
|
518
|
+
var blinkUntil = o.until !== undefined ? +o.until : doneAt + (o.blink === undefined ? 2 : +o.blink);
|
|
519
|
+
for (var bt = doneAt + 0.5, on = false; bt < blinkUntil; bt += 0.5, on = !on) ck.push({ time: bt, properties: { x: endX, opacity: on ? 1 : 0 }, easing: 'linear' });
|
|
520
|
+
if (o.until !== undefined) ck.push({ time: +o.until + 0.4, properties: { x: endX, opacity: 0 } });
|
|
521
|
+
try { P.addAnimation(caretId, ck.map(function (k) { k.easing = k.easing || 'linear'; return k; }), { duration: Math.max(ck[ck.length - 1].time, 0.01), loop: false }); } catch (e) {}
|
|
522
|
+
note(ck[ck.length - 1].time);
|
|
523
|
+
|
|
524
|
+
// placeholder leaves as the first character lands
|
|
525
|
+
var phKeys = windowKeys(o.at, 0.4, null);
|
|
526
|
+
phKeys.push({ t: Math.max(typeAt, (+o.at || 0) + 0.4), opacity: 1 }, { t: Math.max(typeAt, (+o.at || 0) + 0.4) + 0.12, opacity: 0 });
|
|
527
|
+
if (full.length) animate(phList, phKeys, 'linear'); else settle(phList, o);
|
|
528
|
+
settle(baseList, o);
|
|
529
|
+
// the send button presses when typing finishes
|
|
530
|
+
var press = o.press === false || !full.length ? null : doneAt + 0.35;
|
|
531
|
+
var bk = windowKeys(o.at, 0.4, o.until);
|
|
532
|
+
if (press !== null) {
|
|
533
|
+
if (!bk.length) bk.push({ t: 0, opacity: 1 });
|
|
534
|
+
bk.push({ t: press, opacity: 1, scale: 1 }, { t: press + 0.12, opacity: 1, scale: 0.86 }, { t: press + 0.3, opacity: 1, scale: 1 });
|
|
535
|
+
bk.sort(function (p, q) { return p.t - q.t; });
|
|
536
|
+
for (var z = 0; z < bk.length; z++) if (bk[z].scale === undefined) bk[z].scale = 1;
|
|
537
|
+
}
|
|
538
|
+
if (bk.length) animate(btn, bk);
|
|
539
|
+
play();
|
|
540
|
+
var all = baseList.concat(phList, typed, caret, btn);
|
|
541
|
+
return { kind: 'promptBox', ids: ids(all), textId: typedId, typeAt: typeAt, doneAt: doneAt, pressAt: press,
|
|
542
|
+
x: x, y: y, width: width, height: height, top: y - height / 2, bottom: y + height / 2 };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// ---------- toggle ----------------------------------------------------------------
|
|
546
|
+
function toggle(o) {
|
|
547
|
+
o = o || {};
|
|
548
|
+
var th = theme(), cv = canvas(), ph = o.phone && o.phone.kind === 'phone' ? o.phone : null;
|
|
549
|
+
var s = ph ? ph.s : (+o.scale || Math.min(cv.width, cv.height) / 720);
|
|
550
|
+
var h = (+o.size || 31) * s, w = h * 51 / 31;
|
|
551
|
+
var x = o.x !== undefined ? +o.x : cv.width / 2, y = o.y !== undefined ? +o.y : cv.height / 2;
|
|
552
|
+
var dark = !!o.dark;
|
|
553
|
+
var onCol = isColour(o.color) ? o.color : (th.branded ? th.accent : '#34c759');
|
|
554
|
+
var offCol = dark ? '#39393d' : '#e9e9eb';
|
|
555
|
+
var startOn = !!o.on, flip = o.switchAt === undefined || o.switchAt === null ? null : +o.switchAt;
|
|
556
|
+
var list = [];
|
|
557
|
+
var labelIds = [];
|
|
558
|
+
if (o.label) {
|
|
559
|
+
var lbl = [];
|
|
560
|
+
text(lbl, o.label, x - w / 2 - 14 * s, y, 17 * s, isColour(o.labelColor) ? o.labelColor : (ph ? ph.ink : (dark ? '#ffffff' : (ground() ? ink(ground()) : th.text))), 500, 'right', th.body);
|
|
561
|
+
labelIds = lbl; list = list.concat(lbl);
|
|
562
|
+
}
|
|
563
|
+
var base = [];
|
|
564
|
+
rect(base, x, y, w, h, h / 2, offCol);
|
|
565
|
+
var onTrack = [];
|
|
566
|
+
rect(onTrack, x, y, w, h, h / 2, onCol);
|
|
567
|
+
var kr = h / 2 - 2 * s, kxOff = x - w / 2 + 2 * s + kr, kxOn = x + w / 2 - 2 * s - kr;
|
|
568
|
+
var knob = [];
|
|
569
|
+
circle(knob, startOn ? kxOn : kxOff, y + 1.5 * s, kr, '#000000', { opacity: 0.16 }); glyphAlpha(knob, 0.16);
|
|
570
|
+
circle(knob, startOn ? kxOn : kxOff, y, kr, '#ffffff');
|
|
571
|
+
var win = windowKeys(o.at, o.fade, o.until);
|
|
572
|
+
if (labelIds.length && win.length) animate(labelIds, win);
|
|
573
|
+
if (win.length) animate(base, win);
|
|
574
|
+
// on-track opacity and knob x, together with the appearance window
|
|
575
|
+
var times = [0];
|
|
576
|
+
win.forEach(function (k) { times.push(k.t); });
|
|
577
|
+
if (flip !== null) times.push(flip, flip + 0.28);
|
|
578
|
+
times = times.filter(function (t, i, a) { return a.indexOf(t) === i; }).sort(function (a, b) { return a - b; });
|
|
579
|
+
var vis = function (t) {
|
|
580
|
+
if (!win.length) return 1;
|
|
581
|
+
var v = win[0].opacity;
|
|
582
|
+
for (var i = 0; i < win.length; i++) if (win[i].t <= t) v = win[i].opacity;
|
|
583
|
+
for (var j = 1; j < win.length; j++) if (t > win[j - 1].t && t < win[j].t) { var f = (t - win[j - 1].t) / (win[j].t - win[j - 1].t); v = win[j - 1].opacity + (win[j].opacity - win[j - 1].opacity) * f; }
|
|
584
|
+
return v;
|
|
585
|
+
};
|
|
586
|
+
var state = function (t) { if (flip === null || t <= flip) return startOn ? 1 : 0; if (t >= flip + 0.28) return startOn ? 0 : 1; return startOn ? 1 - (t - flip) / 0.28 : (t - flip) / 0.28; };
|
|
587
|
+
if (win.length || flip !== null) {
|
|
588
|
+
try { P.addAnimation(onTrack[0].id, times.map(function (t) { return { time: t, properties: { opacity: vis(t) * state(t) }, easing: 'easeInOut' }; }), { duration: Math.max(times[times.length - 1], 0.01), loop: false }); } catch (e) {}
|
|
589
|
+
knob.forEach(function (it) {
|
|
590
|
+
try { P.addAnimation(it.id, times.map(function (t) { return { time: t, properties: { opacity: vis(t) * (it.alpha === undefined ? 1 : it.alpha), x: kxOff + (kxOn - kxOff) * state(t) }, easing: 'easeInOut' }; }), { duration: Math.max(times[times.length - 1], 0.01), loop: false }); } catch (e) {}
|
|
591
|
+
});
|
|
592
|
+
note(times[times.length - 1]);
|
|
593
|
+
} else if (!startOn) {
|
|
594
|
+
try { P.modifyItem(onTrack[0].id, { opacity: 0 }); } catch (e) {}
|
|
595
|
+
}
|
|
596
|
+
play();
|
|
597
|
+
list = list.concat(base, onTrack, knob);
|
|
598
|
+
return { kind: 'toggle', ids: ids(list), on: flip === null ? startOn : !startOn, switchAt: flip, x: x, y: y, width: w, height: h };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// ---------- calendar card ------------------------------------------------------------
|
|
602
|
+
function calendarCard(o) {
|
|
603
|
+
o = o || {};
|
|
604
|
+
var th = theme(), cv = canvas(), ph = o.phone && o.phone.kind === 'phone' ? o.phone : null;
|
|
605
|
+
var s = ph ? ph.s : (+o.scale || Math.min(cv.width, cv.height) / 720);
|
|
606
|
+
var width = +o.width || (ph ? ph.screen.width - 32 * s : 340 * s);
|
|
607
|
+
var x = o.x !== undefined ? +o.x : (ph ? ph.screen.x : cv.width / 2);
|
|
608
|
+
var events = (o.events || [{ time: '10:00', title: 'Launch review' }, { time: '13:30', title: 'Design sync' }]).slice(0, 5);
|
|
609
|
+
var pad = 18 * s, rowH = 46 * s, headH = 74 * s;
|
|
610
|
+
var h = pad + headH + events.length * rowH + pad * 0.6;
|
|
611
|
+
var top = o.y !== undefined ? +o.y - h / 2 : (ph ? ph.screen.top + 300 * s : cv.height / 2 - h / 2);
|
|
612
|
+
var dark = !!o.dark;
|
|
613
|
+
var bg = dark ? '#1c1c1e' : '#ffffff', fg = dark ? '#ffffff' : '#111111', sub = dark ? '#ebebf5' : '#3c3c43';
|
|
614
|
+
var left = x - width / 2 + pad;
|
|
615
|
+
var list = [];
|
|
616
|
+
rect(list, x, top + h / 2 + 4 * s, width, h, 24 * s * th.radius, '#000000', { opacity: 0.1 }); glyphAlpha(list, 0.1);
|
|
617
|
+
rect(list, x, top + h / 2, width, h, 24 * s * th.radius, bg, { opacity: o.glass ? 0.84 : 1 }); if (o.glass) glyphAlpha(list, 0.84);
|
|
618
|
+
text(list, String(o.day || 'Tuesday').toUpperCase(), left, top + pad + 8 * s, 13 * s, th.primary, 700, 'left', th.body);
|
|
619
|
+
text(list, String(o.date === undefined ? 9 : o.date), left, top + pad + 44 * s, 40 * s, fg, 500, 'left', th.heading);
|
|
620
|
+
if (o.month) { text(list, String(o.month), x + width / 2 - pad, top + pad + 8 * s, 13 * s, sub, 500, 'right', th.body, { opacity: 0.7 }); glyphAlpha(list, 0.7); }
|
|
621
|
+
var colours = [th.primary, th.accent, th.secondary];
|
|
622
|
+
for (var i = 0; i < events.length; i++) {
|
|
623
|
+
var ev = events[i] || {}, ry = top + pad + headH + i * rowH + rowH / 2;
|
|
624
|
+
var col = isColour(ev.color) ? ev.color : colours[i % colours.length];
|
|
625
|
+
rect(list, x, ry, width - pad * 2 + 4 * s, rowH - 8 * s, 8 * s * th.radius, col, { opacity: 0.14 }); glyphAlpha(list, 0.14);
|
|
626
|
+
rect(list, left + 2 * s, ry, 4 * s, rowH - 16 * s, 2 * s, col);
|
|
627
|
+
text(list, fit(ev.title || 'Event', width - pad * 2 - 90 * s, 15 * s, 600, th.body), left + 14 * s, ry - 7 * s, 15 * s, fg, 600, 'left', th.body);
|
|
628
|
+
text(list, String(ev.time || ''), left + 14 * s, ry + 10 * s, 12 * s, sub, 400, 'left', th.body, { opacity: 0.75 }); glyphAlpha(list, 0.75);
|
|
629
|
+
}
|
|
630
|
+
settle(list, o, o.rise === undefined ? (o.at ? 14 * s : 0) : +o.rise);
|
|
631
|
+
return { kind: 'calendarCard', ids: ids(list), x: x, top: top, bottom: top + h, height: h, width: width };
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// ---------- ad card ---------------------------------------------------------------------
|
|
635
|
+
function adCard(o) {
|
|
636
|
+
o = o || {};
|
|
637
|
+
var th = theme(), cv = canvas(), ph = o.phone && o.phone.kind === 'phone' ? o.phone : null;
|
|
638
|
+
var s = ph ? ph.s : (+o.scale || Math.min(cv.width, cv.height) / 720);
|
|
639
|
+
var width = +o.width || (ph ? ph.screen.width - 24 * s : 360 * s);
|
|
640
|
+
var x = o.x !== undefined ? +o.x : (ph ? ph.screen.x : cv.width / 2);
|
|
641
|
+
var pad = 14 * s, headH = 52 * s, mediaH = width * (o.aspect === 'square' ? 1 : 0.75), footH = 64 * s;
|
|
642
|
+
var h = headH + mediaH + footH;
|
|
643
|
+
var top = o.y !== undefined ? +o.y - h / 2 : (ph ? ph.screen.top + 110 * s : cv.height / 2 - h / 2);
|
|
644
|
+
var dark = !!o.dark;
|
|
645
|
+
var bg = dark ? '#1c1c1e' : '#ffffff', fg = dark ? '#ffffff' : '#111111', sub = dark ? '#ebebf5' : '#3c3c43';
|
|
646
|
+
var name = o.brandName || th.name || 'Your Brand';
|
|
647
|
+
var list = [];
|
|
648
|
+
var R = 20 * s * th.radius;
|
|
649
|
+
rect(list, x, top + h / 2 + 5 * s, width, h, R, '#000000', { opacity: 0.12 }); glyphAlpha(list, 0.12);
|
|
650
|
+
rect(list, x, top + h / 2, width, h, R, bg);
|
|
651
|
+
var left = x - width / 2 + pad, av = 32 * s;
|
|
652
|
+
circle(list, left + av / 2, top + headH / 2, av / 2, th.primary);
|
|
653
|
+
text(list, (o.logo || name.charAt(0)).slice(0, 2), left + av / 2, top + headH / 2, 15 * s, ink(th.primary), 700, 'center', th.heading);
|
|
654
|
+
text(list, fit(name, width - av - pad * 3 - 20 * s, 15 * s, 600, th.body), left + av + 10 * s, top + headH / 2 - 8 * s, 15 * s, fg, 600, 'left', th.body);
|
|
655
|
+
text(list, o.sponsored === false ? (o.handle || '') : (o.sponsoredLabel || 'Sponsored'), left + av + 10 * s, top + headH / 2 + 9 * s, 12 * s, sub, 400, 'left', th.body, { opacity: 0.7 }); glyphAlpha(list, 0.7);
|
|
656
|
+
for (var d = 0; d < 3; d++) circle(list, x + width / 2 - pad - 4 * s - d * 6 * s, top + headH / 2, 1.8 * s, sub);
|
|
657
|
+
var mTop = top + headH, mStops = Array.isArray(o.media) ? o.media.filter(isColour) : (isColour(o.media) ? [o.media] : [th.primary, th.secondary]);
|
|
658
|
+
if (!mStops.length) mStops = [th.primary, th.secondary];
|
|
659
|
+
var mFill = mStops.length > 1 ? { gradient: { stops: mStops }, origin: [x - width / 2, mTop], destination: [x + width / 2, mTop + mediaH] } : mStops[0];
|
|
660
|
+
rect(list, x, mTop + mediaH / 2, width, mediaH, 0, mFill);
|
|
661
|
+
var mInk = ink(mStops.length > 1 ? mix(mStops[0], mStops[mStops.length - 1], 0.5) : mStops[0]);
|
|
662
|
+
var hl = wrap(o.headline || 'Meet the new way to work', width - pad * 4, 30 * s, 700, th.heading, 3);
|
|
663
|
+
var hlTop = mTop + mediaH / 2 - (hl.length - 1) * 30 * s * 0.6;
|
|
664
|
+
for (var l = 0; l < hl.length; l++) text(list, hl[l], x, hlTop + l * 30 * s * 1.2, 30 * s, mInk, 700, 'center', th.heading);
|
|
665
|
+
var fy = mTop + mediaH + footH / 2;
|
|
666
|
+
var cta = o.cta || 'Learn more';
|
|
667
|
+
var ctaFs = 14 * s, ctaW = measure(cta, ctaFs, 600, th.body) + 28 * s, ctaH = 34 * s;
|
|
668
|
+
var ctaX = x + width / 2 - pad - ctaW / 2;
|
|
669
|
+
text(list, fit(o.body || name, width - ctaW - pad * 3, 14 * s, 500, th.body), left, fy, 14 * s, fg, 500, 'left', th.body);
|
|
670
|
+
rect(list, ctaX, fy, ctaW, ctaH, Math.min(ctaH / 2, 10 * s * th.radius * 1.7), th.primary);
|
|
671
|
+
text(list, cta, ctaX, fy, ctaFs, ink(th.primary), 600, 'center', th.body);
|
|
672
|
+
settle(list, o, o.rise === undefined ? (o.at ? 18 * s : 0) : +o.rise);
|
|
673
|
+
return { kind: 'adCard', ids: ids(list), x: x, top: top, bottom: top + h, height: h, width: width };
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// ---------- brand -------------------------------------------------------------------------
|
|
677
|
+
/** Set the brand the kit draws in: the brand kit's own shape { name, colors: { primary, secondary,
|
|
678
|
+
* accent, background, text }, fonts: { heading, body }, radius: 'sharp'|'soft'|'round'|number }. */
|
|
679
|
+
function setBrand(kit) {
|
|
680
|
+
if (kit === null) { brand = null; return theme(); }
|
|
681
|
+
if (kit && typeof kit === 'object') {
|
|
682
|
+
var colours = kit.colors || kit.colours || {};
|
|
683
|
+
if (Array.isArray(colours)) colours = { primary: colours[0], secondary: colours[1], accent: colours[2] };
|
|
684
|
+
brand = { name: kit.name || '', colors: colours, fonts: kit.fonts || {}, radius: kit.radius };
|
|
685
|
+
}
|
|
686
|
+
return theme();
|
|
687
|
+
}
|
|
688
|
+
// A brand kit applied in code also becomes the kit's brand. Wrapped once, and the engine's own
|
|
689
|
+
// behaviour is kept exactly: the wrapper records and then calls through.
|
|
690
|
+
try {
|
|
691
|
+
if (P && typeof P.applyBrandKit === 'function' && !P.applyBrandKit.__uiKit) {
|
|
692
|
+
var orig = P.applyBrandKit;
|
|
693
|
+
var wrapped = function (kit) { try { if (kit && kit.colors && kit.colors.primary) setBrand(kit); } catch (e) {} return orig.apply(this, arguments); };
|
|
694
|
+
wrapped.__uiKit = true;
|
|
695
|
+
P.applyBrandKit = wrapped;
|
|
696
|
+
}
|
|
697
|
+
} catch (e) {}
|
|
698
|
+
|
|
699
|
+
return {
|
|
700
|
+
version: '1.0.0',
|
|
701
|
+
brand: setBrand,
|
|
702
|
+
theme: theme,
|
|
703
|
+
duration: function (D) { if (D !== undefined) { minDuration = +D || 0; play(); } return Math.max(minDuration, kitEnd > 0 ? kitEnd + HOLD : 0); },
|
|
704
|
+
phone: phone,
|
|
705
|
+
notifications: notifications,
|
|
706
|
+
notification: notification,
|
|
707
|
+
promptBox: promptBox,
|
|
708
|
+
toggle: toggle,
|
|
709
|
+
calendarCard: calendarCard,
|
|
710
|
+
adCard: adCard
|
|
711
|
+
};
|
|
712
|
+
})();
|
|
713
|
+
// --- END PINEPAPER UI KIT -----------------------------------------------------------
|
|
714
|
+
`;
|
|
715
|
+
/** Does this code call the kit without carrying it? */
|
|
716
|
+
export function usesUiKit(code) {
|
|
717
|
+
const src = String(code ?? "");
|
|
718
|
+
if (!src)
|
|
719
|
+
return false;
|
|
720
|
+
if (src.includes(UI_KIT_SENTINEL))
|
|
721
|
+
return false;
|
|
722
|
+
// Code that defines its own UIKit is left alone: prepending would be shadowed or would shadow.
|
|
723
|
+
if (/(?:var|let|const)\s+UIKit\s*=|function\s+UIKit\s*\(/.test(src))
|
|
724
|
+
return false;
|
|
725
|
+
return UI_KIT_SYMBOLS.some((s) => src.includes(s));
|
|
726
|
+
}
|
|
727
|
+
const COLOUR = /^(#[0-9a-f]{3,8}|rgba?\([\d\s.,%]+\)|hsla?\([\d\s.,%deg]+\))$/i;
|
|
728
|
+
const FONT = /^[\w \-]{1,60}$/;
|
|
729
|
+
/**
|
|
730
|
+
* The one line that tells the kit which brand it is drawing in, built from a scene's brand_kit op.
|
|
731
|
+
* Every value is checked where it enters (this becomes code): colours must be colours, fonts plain
|
|
732
|
+
* family names, and the whole thing travels as JSON.
|
|
733
|
+
*/
|
|
734
|
+
export function uiKitBrandLine(kit) {
|
|
735
|
+
if (!kit || typeof kit !== "object")
|
|
736
|
+
return null;
|
|
737
|
+
const k = kit;
|
|
738
|
+
const colors = {};
|
|
739
|
+
for (const role of ["primary", "secondary", "accent", "background", "text"]) {
|
|
740
|
+
const v = k.colors?.[role];
|
|
741
|
+
if (typeof v === "string" && COLOUR.test(v.trim()))
|
|
742
|
+
colors[role] = v.trim();
|
|
743
|
+
}
|
|
744
|
+
if (!colors.primary)
|
|
745
|
+
return null;
|
|
746
|
+
const fonts = {};
|
|
747
|
+
for (const role of ["heading", "body", "mono"]) {
|
|
748
|
+
const v = k.fonts?.[role];
|
|
749
|
+
if (typeof v === "string" && FONT.test(v.trim()))
|
|
750
|
+
fonts[role] = v.trim();
|
|
751
|
+
}
|
|
752
|
+
const out = { name: typeof k.name === "string" ? k.name.slice(0, 80) : "", colors, fonts };
|
|
753
|
+
if (typeof k.radius === "number" && Number.isFinite(k.radius))
|
|
754
|
+
out.radius = Math.max(0, Math.min(2, k.radius));
|
|
755
|
+
else if (k.radius === "sharp" || k.radius === "soft" || k.radius === "round")
|
|
756
|
+
out.radius = k.radius;
|
|
757
|
+
const LS = new RegExp(String.fromCharCode(0x2028), "g"), PS = new RegExp(String.fromCharCode(0x2029), "g");
|
|
758
|
+
const json = JSON.stringify(out).replace(/</g, "\\u003c").replace(LS, "\\u2028").replace(PS, "\\u2029");
|
|
759
|
+
return `UIKit.brand(${json});`;
|
|
760
|
+
}
|
|
761
|
+
//# sourceMappingURL=ui-kit-runtime.js.map
|