@hajimism/dek 0.1.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.
- package/LICENSE +21 -0
- package/README.ja.md +75 -0
- package/README.md +75 -0
- package/package.json +65 -0
- package/src/cli/address.ts +53 -0
- package/src/cli/build.ts +85 -0
- package/src/cli/check.ts +97 -0
- package/src/cli/commands.ts +851 -0
- package/src/cli/contract.ts +444 -0
- package/src/cli/cues.ts +18 -0
- package/src/cli/files.ts +309 -0
- package/src/cli/flags.ts +295 -0
- package/src/cli/format.ts +156 -0
- package/src/cli/goto.ts +96 -0
- package/src/cli/init.ts +136 -0
- package/src/cli/keep-alive.ts +72 -0
- package/src/cli/lint.ts +84 -0
- package/src/cli/ls.ts +119 -0
- package/src/cli/main.ts +231 -0
- package/src/cli/marks.ts +16 -0
- package/src/cli/mv.ts +37 -0
- package/src/cli/new.ts +64 -0
- package/src/cli/pdf.ts +16 -0
- package/src/cli/qr.ts +466 -0
- package/src/cli/ref.ts +165 -0
- package/src/cli/rehearse.ts +42 -0
- package/src/cli/result.ts +161 -0
- package/src/cli/scope.ts +308 -0
- package/src/cli/serve.ts +125 -0
- package/src/cli/shot.ts +134 -0
- package/src/cli/show.ts +103 -0
- package/src/cli/sync.ts +37 -0
- package/src/cli/text.ts +479 -0
- package/src/cli/theme.ts +56 -0
- package/src/cli/tty.ts +81 -0
- package/src/cli/usage.ts +198 -0
- package/src/cli/video.ts +17 -0
- package/src/cli/voice.ts +113 -0
- package/src/cli.ts +4 -0
- package/src/core/assets.ts +239 -0
- package/src/core/build.ts +77 -0
- package/src/core/bundled-theme.ts +15 -0
- package/src/core/capture-go.ts +72 -0
- package/src/core/chrome.ts +128 -0
- package/src/core/config-keys.ts +82 -0
- package/src/core/config.ts +95 -0
- package/src/core/css-scan.ts +158 -0
- package/src/core/css-transform.ts +140 -0
- package/src/core/css-value.ts +127 -0
- package/src/core/css.ts +545 -0
- package/src/core/cue.ts +187 -0
- package/src/core/deck-paths.ts +34 -0
- package/src/core/diagnostic.ts +114 -0
- package/src/core/document.ts +218 -0
- package/src/core/error.ts +31 -0
- package/src/core/escape.ts +16 -0
- package/src/core/fill.ts +100 -0
- package/src/core/finish-beat.ts +30 -0
- package/src/core/github.ts +234 -0
- package/src/core/html-scan.ts +253 -0
- package/src/core/html.ts +234 -0
- package/src/core/in-page-go.ts +128 -0
- package/src/core/lines.ts +43 -0
- package/src/core/lint/asset-refs.ts +109 -0
- package/src/core/lint/context.ts +116 -0
- package/src/core/lint/files.ts +117 -0
- package/src/core/lint/keys.ts +48 -0
- package/src/core/lint/morph.ts +91 -0
- package/src/core/lint/script.ts +102 -0
- package/src/core/lint/shared-styles.ts +165 -0
- package/src/core/lint/slide-html.ts +466 -0
- package/src/core/lint/slide-script.ts +179 -0
- package/src/core/lint/theme-css.ts +295 -0
- package/src/core/lint/timing.ts +66 -0
- package/src/core/lint/tokens.ts +520 -0
- package/src/core/lint/voice.ts +76 -0
- package/src/core/lint.ts +137 -0
- package/src/core/live-protocol.ts +130 -0
- package/src/core/marks.ts +198 -0
- package/src/core/mv.ts +293 -0
- package/src/core/ogp.ts +99 -0
- package/src/core/optional.ts +24 -0
- package/src/core/overflow.ts +220 -0
- package/src/core/page-pool.ts +53 -0
- package/src/core/page.ts +66 -0
- package/src/core/parse.ts +307 -0
- package/src/core/path.ts +58 -0
- package/src/core/pdf.ts +71 -0
- package/src/core/playwright-module.d.ts +43 -0
- package/src/core/playwright-motion.ts +42 -0
- package/src/core/playwright-sheet.ts +32 -0
- package/src/core/playwright-visual.ts +391 -0
- package/src/core/playwright-worker.ts +5 -0
- package/src/core/playwright.ts +431 -0
- package/src/core/position.ts +146 -0
- package/src/core/presenter-state.ts +43 -0
- package/src/core/presenter.ts +131 -0
- package/src/core/prose.ts +16 -0
- package/src/core/pseudo-text.ts +67 -0
- package/src/core/rail-width.ts +4 -0
- package/src/core/ref-name.ts +110 -0
- package/src/core/ref.ts +238 -0
- package/src/core/resolve.ts +228 -0
- package/src/core/rumdl.ts +99 -0
- package/src/core/safe-fs.ts +196 -0
- package/src/core/sarif.ts +135 -0
- package/src/core/schema.ts +89 -0
- package/src/core/script-lines.ts +53 -0
- package/src/core/sheet.ts +223 -0
- package/src/core/shot/morph.ts +80 -0
- package/src/core/shot/motion.ts +187 -0
- package/src/core/shot/still.ts +216 -0
- package/src/core/shot-cache.ts +140 -0
- package/src/core/size.ts +11 -0
- package/src/core/skeleton-record.ts +51 -0
- package/src/core/skeleton.ts +89 -0
- package/src/core/slide-draw.ts +165 -0
- package/src/core/slide-eval-worker.ts +58 -0
- package/src/core/slide-measure.ts +272 -0
- package/src/core/slide-script-eval.ts +258 -0
- package/src/core/slide-script.ts +252 -0
- package/src/core/spawn.ts +128 -0
- package/src/core/step.ts +94 -0
- package/src/core/still-page.ts +70 -0
- package/src/core/suggest.ts +33 -0
- package/src/core/sync.ts +316 -0
- package/src/core/text-contrast.ts +466 -0
- package/src/core/theme-excerpt.ts +187 -0
- package/src/core/theme-facts.ts +51 -0
- package/src/core/timeline.ts +216 -0
- package/src/core/timing.ts +88 -0
- package/src/core/toml-keys.ts +349 -0
- package/src/core/url-attributes.ts +103 -0
- package/src/core/video-document.ts +15 -0
- package/src/core/visual-cache.ts +100 -0
- package/src/core/visual.ts +558 -0
- package/src/core/voice.ts +321 -0
- package/src/core/zod.ts +79 -0
- package/src/runtime/browser.ts +203 -0
- package/src/runtime/clone.ts +52 -0
- package/src/runtime/deck-control.ts +43 -0
- package/src/runtime/fit.ts +32 -0
- package/src/runtime/go.ts +56 -0
- package/src/runtime/input.ts +128 -0
- package/src/runtime/laser-view.ts +110 -0
- package/src/runtime/laser.ts +145 -0
- package/src/runtime/live-host.ts +33 -0
- package/src/runtime/live-reload.ts +29 -0
- package/src/runtime/live.ts +88 -0
- package/src/runtime/marks-view.ts +74 -0
- package/src/runtime/motion.ts +113 -0
- package/src/runtime/navigator.ts +189 -0
- package/src/runtime/player.ts +28 -0
- package/src/runtime/presenter-view.ts +289 -0
- package/src/runtime/presenter.ts +52 -0
- package/src/runtime/rail-view.ts +229 -0
- package/src/runtime/rail.ts +52 -0
- package/src/runtime/rehearse.ts +264 -0
- package/src/runtime/routes.ts +142 -0
- package/src/runtime/settle.ts +45 -0
- package/src/runtime/slide.d.ts +28 -0
- package/src/runtime/socket.ts +143 -0
- package/src/runtime/stage.ts +259 -0
- package/src/runtime/step.ts +139 -0
- package/src/runtime/window.ts +38 -0
- package/src/server/auth.ts +210 -0
- package/src/server/deck-snapshot.ts +146 -0
- package/src/server/dev.ts +251 -0
- package/src/server/hub.ts +104 -0
- package/src/server/lan.ts +51 -0
- package/src/server/lock.ts +97 -0
- package/src/server/pages.ts +71 -0
- package/src/server/pairing.ts +40 -0
- package/src/server/project-watch.ts +142 -0
- package/src/server/rooms.ts +133 -0
- package/src/server/routes.ts +286 -0
- package/src/server/serial.ts +25 -0
- package/src/server/watch.ts +308 -0
- package/src/theme/default.css +175 -0
- package/src/video/bake.ts +152 -0
- package/src/video/mux.ts +103 -0
- package/src/video/recorder.ts +115 -0
- package/src/video/sidecar.ts +60 -0
- package/src/video/worker.ts +51 -0
- package/src/voice/engine.ts +177 -0
- package/src/voice/synth.ts +127 -0
- package/src/voice/wav.ts +115 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
import { type CssValue, parseCssValue } from "../css-value.ts";
|
|
2
|
+
|
|
3
|
+
export const REQUIRED_TOKENS = [
|
|
4
|
+
"--fg",
|
|
5
|
+
"--bg",
|
|
6
|
+
"--accent",
|
|
7
|
+
"--muted",
|
|
8
|
+
"--font-title",
|
|
9
|
+
"--font-body",
|
|
10
|
+
"--size-title",
|
|
11
|
+
"--size-body",
|
|
12
|
+
"--size-caption",
|
|
13
|
+
"--gap",
|
|
14
|
+
"--pad",
|
|
15
|
+
"--radius",
|
|
16
|
+
"--step-transition",
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Units that fix a size outside the theme: absolute lengths, the viewport's and a container's
|
|
21
|
+
* units, the root's, and time. Units relative to the element's own font (em, ch, lh, ex, cap, ic)
|
|
22
|
+
* follow the theme's type and stay.
|
|
23
|
+
*/
|
|
24
|
+
const RAW_UNITS = new Set(
|
|
25
|
+
[
|
|
26
|
+
"px",
|
|
27
|
+
"pt",
|
|
28
|
+
"pc",
|
|
29
|
+
"cm",
|
|
30
|
+
"mm",
|
|
31
|
+
"in",
|
|
32
|
+
"q",
|
|
33
|
+
"rem",
|
|
34
|
+
"rlh",
|
|
35
|
+
"rcap",
|
|
36
|
+
"rch",
|
|
37
|
+
"rex",
|
|
38
|
+
"ric",
|
|
39
|
+
"vw",
|
|
40
|
+
"vh",
|
|
41
|
+
"vi",
|
|
42
|
+
"vb",
|
|
43
|
+
"vmin",
|
|
44
|
+
"vmax",
|
|
45
|
+
...["s", "l", "d"].flatMap((kind) =>
|
|
46
|
+
["w", "h", "i", "b", "min", "max"].map((axis) => `${kind}v${axis}`),
|
|
47
|
+
),
|
|
48
|
+
"cqw",
|
|
49
|
+
"cqh",
|
|
50
|
+
"cqi",
|
|
51
|
+
"cqb",
|
|
52
|
+
"cqmin",
|
|
53
|
+
"cqmax",
|
|
54
|
+
"ms",
|
|
55
|
+
"s",
|
|
56
|
+
].map((unit) => unit.toLowerCase()),
|
|
57
|
+
);
|
|
58
|
+
const TIME_UNITS = new Set(["ms", "s"]);
|
|
59
|
+
|
|
60
|
+
const COLOR_FUNCTIONS = new Set([
|
|
61
|
+
"rgb",
|
|
62
|
+
"rgba",
|
|
63
|
+
"hsl",
|
|
64
|
+
"hsla",
|
|
65
|
+
"hwb",
|
|
66
|
+
"lab",
|
|
67
|
+
"lch",
|
|
68
|
+
"oklab",
|
|
69
|
+
"oklch",
|
|
70
|
+
"color",
|
|
71
|
+
"color-mix",
|
|
72
|
+
"light-dark",
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
/** An easing written out, which motion takes from a token like every other value. */
|
|
76
|
+
const EASING_FUNCTIONS = new Set(["cubic-bezier", "steps", "linear"]);
|
|
77
|
+
|
|
78
|
+
const GLOBAL_KEYWORD_RE = /^(inherit|initial|unset|revert|revert-layer)$/i;
|
|
79
|
+
|
|
80
|
+
/** What a `font` shorthand may say besides its family: style, weight, stretch, and system fonts. */
|
|
81
|
+
const FONT_KEYWORDS = new Set([
|
|
82
|
+
"normal",
|
|
83
|
+
"italic",
|
|
84
|
+
"oblique",
|
|
85
|
+
"small-caps",
|
|
86
|
+
"bold",
|
|
87
|
+
"bolder",
|
|
88
|
+
"lighter",
|
|
89
|
+
"ultra-condensed",
|
|
90
|
+
"extra-condensed",
|
|
91
|
+
"condensed",
|
|
92
|
+
"semi-condensed",
|
|
93
|
+
"semi-expanded",
|
|
94
|
+
"expanded",
|
|
95
|
+
"extra-expanded",
|
|
96
|
+
"ultra-expanded",
|
|
97
|
+
"caption",
|
|
98
|
+
"icon",
|
|
99
|
+
"menu",
|
|
100
|
+
"message-box",
|
|
101
|
+
"small-caption",
|
|
102
|
+
"status-bar",
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
/** The colors the platform names after its own widgets, which no theme controls. */
|
|
106
|
+
const SYSTEM_COLORS = new Set(
|
|
107
|
+
[
|
|
108
|
+
"AccentColor",
|
|
109
|
+
"AccentColorText",
|
|
110
|
+
"ActiveText",
|
|
111
|
+
"ButtonBorder",
|
|
112
|
+
"ButtonFace",
|
|
113
|
+
"ButtonText",
|
|
114
|
+
"Canvas",
|
|
115
|
+
"CanvasText",
|
|
116
|
+
"Field",
|
|
117
|
+
"FieldText",
|
|
118
|
+
"GrayText",
|
|
119
|
+
"Highlight",
|
|
120
|
+
"HighlightText",
|
|
121
|
+
"LinkText",
|
|
122
|
+
"Mark",
|
|
123
|
+
"MarkText",
|
|
124
|
+
"SelectedItem",
|
|
125
|
+
"SelectedItemText",
|
|
126
|
+
"VisitedText",
|
|
127
|
+
"ActiveBorder",
|
|
128
|
+
"ActiveCaption",
|
|
129
|
+
"AppWorkspace",
|
|
130
|
+
"Background",
|
|
131
|
+
"ButtonHighlight",
|
|
132
|
+
"ButtonShadow",
|
|
133
|
+
"CaptionText",
|
|
134
|
+
"InactiveBorder",
|
|
135
|
+
"InactiveCaption",
|
|
136
|
+
"InactiveCaptionText",
|
|
137
|
+
"InfoBackground",
|
|
138
|
+
"InfoText",
|
|
139
|
+
"Menu",
|
|
140
|
+
"MenuText",
|
|
141
|
+
"Scrollbar",
|
|
142
|
+
"ThreeDDarkShadow",
|
|
143
|
+
"ThreeDFace",
|
|
144
|
+
"ThreeDHighlight",
|
|
145
|
+
"ThreeDLightShadow",
|
|
146
|
+
"ThreeDShadow",
|
|
147
|
+
"Window",
|
|
148
|
+
"WindowFrame",
|
|
149
|
+
"WindowText",
|
|
150
|
+
].map((name) => name.toLowerCase()),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
/** Shorthands and image properties whose values may hold a color among other things. */
|
|
154
|
+
const COLOR_HOLDERS = new Set([
|
|
155
|
+
"background",
|
|
156
|
+
"background-image",
|
|
157
|
+
"border",
|
|
158
|
+
"border-top",
|
|
159
|
+
"border-right",
|
|
160
|
+
"border-bottom",
|
|
161
|
+
"border-left",
|
|
162
|
+
"border-block",
|
|
163
|
+
"border-block-start",
|
|
164
|
+
"border-block-end",
|
|
165
|
+
"border-inline",
|
|
166
|
+
"border-inline-start",
|
|
167
|
+
"border-inline-end",
|
|
168
|
+
"border-image",
|
|
169
|
+
"outline",
|
|
170
|
+
"text-decoration",
|
|
171
|
+
"text-emphasis",
|
|
172
|
+
"column-rule",
|
|
173
|
+
"box-shadow",
|
|
174
|
+
"text-shadow",
|
|
175
|
+
"fill",
|
|
176
|
+
"stroke",
|
|
177
|
+
"filter",
|
|
178
|
+
"backdrop-filter",
|
|
179
|
+
"mask",
|
|
180
|
+
"mask-image",
|
|
181
|
+
"list-style",
|
|
182
|
+
"-webkit-text-stroke",
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
/** Whether a name in this property's value can be a color: `tan` in `grid-area` is an area. */
|
|
186
|
+
function takesColor(property: string): boolean {
|
|
187
|
+
const name = property.toLowerCase();
|
|
188
|
+
return name.startsWith("--") || /(^|-)color$/.test(name) || COLOR_HOLDERS.has(name);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const NAMED_COLORS = new Set([
|
|
192
|
+
"aliceblue",
|
|
193
|
+
"antiquewhite",
|
|
194
|
+
"aqua",
|
|
195
|
+
"aquamarine",
|
|
196
|
+
"azure",
|
|
197
|
+
"beige",
|
|
198
|
+
"bisque",
|
|
199
|
+
"black",
|
|
200
|
+
"blanchedalmond",
|
|
201
|
+
"blue",
|
|
202
|
+
"blueviolet",
|
|
203
|
+
"brown",
|
|
204
|
+
"burlywood",
|
|
205
|
+
"cadetblue",
|
|
206
|
+
"chartreuse",
|
|
207
|
+
"chocolate",
|
|
208
|
+
"coral",
|
|
209
|
+
"cornflowerblue",
|
|
210
|
+
"cornsilk",
|
|
211
|
+
"crimson",
|
|
212
|
+
"cyan",
|
|
213
|
+
"darkblue",
|
|
214
|
+
"darkcyan",
|
|
215
|
+
"darkgoldenrod",
|
|
216
|
+
"darkgray",
|
|
217
|
+
"darkgreen",
|
|
218
|
+
"darkgrey",
|
|
219
|
+
"darkkhaki",
|
|
220
|
+
"darkmagenta",
|
|
221
|
+
"darkolivegreen",
|
|
222
|
+
"darkorange",
|
|
223
|
+
"darkorchid",
|
|
224
|
+
"darkred",
|
|
225
|
+
"darksalmon",
|
|
226
|
+
"darkseagreen",
|
|
227
|
+
"darkslateblue",
|
|
228
|
+
"darkslategray",
|
|
229
|
+
"darkslategrey",
|
|
230
|
+
"darkturquoise",
|
|
231
|
+
"darkviolet",
|
|
232
|
+
"deeppink",
|
|
233
|
+
"deepskyblue",
|
|
234
|
+
"dimgray",
|
|
235
|
+
"dimgrey",
|
|
236
|
+
"dodgerblue",
|
|
237
|
+
"firebrick",
|
|
238
|
+
"floralwhite",
|
|
239
|
+
"forestgreen",
|
|
240
|
+
"fuchsia",
|
|
241
|
+
"gainsboro",
|
|
242
|
+
"ghostwhite",
|
|
243
|
+
"gold",
|
|
244
|
+
"goldenrod",
|
|
245
|
+
"gray",
|
|
246
|
+
"green",
|
|
247
|
+
"greenyellow",
|
|
248
|
+
"grey",
|
|
249
|
+
"honeydew",
|
|
250
|
+
"hotpink",
|
|
251
|
+
"indianred",
|
|
252
|
+
"indigo",
|
|
253
|
+
"ivory",
|
|
254
|
+
"khaki",
|
|
255
|
+
"lavender",
|
|
256
|
+
"lavenderblush",
|
|
257
|
+
"lawngreen",
|
|
258
|
+
"lemonchiffon",
|
|
259
|
+
"lightblue",
|
|
260
|
+
"lightcoral",
|
|
261
|
+
"lightcyan",
|
|
262
|
+
"lightgoldenrodyellow",
|
|
263
|
+
"lightgray",
|
|
264
|
+
"lightgreen",
|
|
265
|
+
"lightgrey",
|
|
266
|
+
"lightpink",
|
|
267
|
+
"lightsalmon",
|
|
268
|
+
"lightseagreen",
|
|
269
|
+
"lightskyblue",
|
|
270
|
+
"lightslategray",
|
|
271
|
+
"lightslategrey",
|
|
272
|
+
"lightsteelblue",
|
|
273
|
+
"lightyellow",
|
|
274
|
+
"lime",
|
|
275
|
+
"limegreen",
|
|
276
|
+
"linen",
|
|
277
|
+
"magenta",
|
|
278
|
+
"maroon",
|
|
279
|
+
"mediumaquamarine",
|
|
280
|
+
"mediumblue",
|
|
281
|
+
"mediumorchid",
|
|
282
|
+
"mediumpurple",
|
|
283
|
+
"mediumseagreen",
|
|
284
|
+
"mediumslateblue",
|
|
285
|
+
"mediumspringgreen",
|
|
286
|
+
"mediumturquoise",
|
|
287
|
+
"mediumvioletred",
|
|
288
|
+
"midnightblue",
|
|
289
|
+
"mintcream",
|
|
290
|
+
"mistyrose",
|
|
291
|
+
"moccasin",
|
|
292
|
+
"navajowhite",
|
|
293
|
+
"navy",
|
|
294
|
+
"oldlace",
|
|
295
|
+
"olive",
|
|
296
|
+
"olivedrab",
|
|
297
|
+
"orange",
|
|
298
|
+
"orangered",
|
|
299
|
+
"orchid",
|
|
300
|
+
"palegoldenrod",
|
|
301
|
+
"palegreen",
|
|
302
|
+
"paleturquoise",
|
|
303
|
+
"palevioletred",
|
|
304
|
+
"papayawhip",
|
|
305
|
+
"peachpuff",
|
|
306
|
+
"peru",
|
|
307
|
+
"pink",
|
|
308
|
+
"plum",
|
|
309
|
+
"powderblue",
|
|
310
|
+
"purple",
|
|
311
|
+
"rebeccapurple",
|
|
312
|
+
"red",
|
|
313
|
+
"rosybrown",
|
|
314
|
+
"royalblue",
|
|
315
|
+
"saddlebrown",
|
|
316
|
+
"salmon",
|
|
317
|
+
"sandybrown",
|
|
318
|
+
"seagreen",
|
|
319
|
+
"seashell",
|
|
320
|
+
"sienna",
|
|
321
|
+
"silver",
|
|
322
|
+
"skyblue",
|
|
323
|
+
"slateblue",
|
|
324
|
+
"slategray",
|
|
325
|
+
"slategrey",
|
|
326
|
+
"snow",
|
|
327
|
+
"springgreen",
|
|
328
|
+
"steelblue",
|
|
329
|
+
"tan",
|
|
330
|
+
"teal",
|
|
331
|
+
"thistle",
|
|
332
|
+
"tomato",
|
|
333
|
+
"turquoise",
|
|
334
|
+
"violet",
|
|
335
|
+
"wheat",
|
|
336
|
+
"white",
|
|
337
|
+
"whitesmoke",
|
|
338
|
+
"yellow",
|
|
339
|
+
"yellowgreen",
|
|
340
|
+
]);
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Whether a declaration writes a design value itself instead of taking it from a token: a color,
|
|
344
|
+
* a length or a time in a unit the theme decides, an easing, or a font family. A custom property
|
|
345
|
+
* is where a token is defined, so it is judged where it is set; see `isRawTokenValue`.
|
|
346
|
+
*/
|
|
347
|
+
export function isRawThemeValue(property: string, value: string): boolean {
|
|
348
|
+
if (property.startsWith("--")) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
const values = parseCssValue(value);
|
|
352
|
+
const name = property.toLowerCase();
|
|
353
|
+
if (hasRawPart(values, takesColor(name))) {
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
// A family is raw whether it is quoted or not; so is the family a `font` shorthand names.
|
|
357
|
+
if (name === "font-family" || name === "font") {
|
|
358
|
+
return values.some(
|
|
359
|
+
(part) =>
|
|
360
|
+
part.kind === "string" ||
|
|
361
|
+
(part.kind === "ident" &&
|
|
362
|
+
!GLOBAL_KEYWORD_RE.test(part.value) &&
|
|
363
|
+
(name === "font-family" || !FONT_KEYWORDS.has(part.value.toLowerCase()))),
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** Whether a custom property's value is a raw design value rather than one taken from a token. */
|
|
370
|
+
export function isRawTokenValue(value: string): boolean {
|
|
371
|
+
return hasRawPart(parseCssValue(value), true);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Whether any part of a value is a raw design value. Inside `var()` only a fallback counts, since
|
|
376
|
+
* a fallback is a value written out; inside `url()` nothing does.
|
|
377
|
+
*/
|
|
378
|
+
function hasRawPart(values: CssValue[], colors: boolean): boolean {
|
|
379
|
+
return values.some((part) => {
|
|
380
|
+
switch (part.kind) {
|
|
381
|
+
case "dimension":
|
|
382
|
+
return RAW_UNITS.has(part.unit.toLowerCase());
|
|
383
|
+
case "hash":
|
|
384
|
+
return true;
|
|
385
|
+
case "ident":
|
|
386
|
+
return colors && isColorName(part.value);
|
|
387
|
+
case "function":
|
|
388
|
+
if (part.name === "var") {
|
|
389
|
+
return part.args.some((arg) => arg.kind === "delim" && arg.value === ",");
|
|
390
|
+
}
|
|
391
|
+
return (
|
|
392
|
+
COLOR_FUNCTIONS.has(part.name) ||
|
|
393
|
+
EASING_FUNCTIONS.has(part.name) ||
|
|
394
|
+
hasRawPart(part.args, colors)
|
|
395
|
+
);
|
|
396
|
+
default:
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function isColorName(ident: string): boolean {
|
|
403
|
+
const name = ident.toLowerCase();
|
|
404
|
+
return NAMED_COLORS.has(name) || SYSTEM_COLORS.has(name);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
type TokenKind = "color" | "font" | "size" | "radius" | "space" | "time" | "easing";
|
|
408
|
+
|
|
409
|
+
const MAX_HINT_TOKENS = 6;
|
|
410
|
+
|
|
411
|
+
/** Whether a value, outside `var()`, holds a color: a hash, a color function, or a named one. */
|
|
412
|
+
function isColorValue(values: CssValue[]): boolean {
|
|
413
|
+
return values.some(
|
|
414
|
+
(part) =>
|
|
415
|
+
part.kind === "hash" ||
|
|
416
|
+
(part.kind === "ident" && isColorName(part.value)) ||
|
|
417
|
+
(part.kind === "function" &&
|
|
418
|
+
part.name !== "var" &&
|
|
419
|
+
(COLOR_FUNCTIONS.has(part.name) || isColorValue(part.args))),
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function hasUnit(values: CssValue[], units: Set<string>): boolean {
|
|
424
|
+
return values.some(
|
|
425
|
+
(part) =>
|
|
426
|
+
(part.kind === "dimension" && units.has(part.unit.toLowerCase())) ||
|
|
427
|
+
(part.kind === "function" && part.name !== "var" && hasUnit(part.args, units)),
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function hasEasing(values: CssValue[]): boolean {
|
|
432
|
+
return values.some(
|
|
433
|
+
(part) =>
|
|
434
|
+
part.kind === "function" &&
|
|
435
|
+
part.name !== "var" &&
|
|
436
|
+
(EASING_FUNCTIONS.has(part.name) || hasEasing(part.args)),
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** What a token holds, judged from its name and the value the theme gives it. */
|
|
441
|
+
function tokenKind(name: string, value: string): TokenKind | undefined {
|
|
442
|
+
const values = parseCssValue(value);
|
|
443
|
+
if (isColorValue(values)) {
|
|
444
|
+
return "color";
|
|
445
|
+
}
|
|
446
|
+
// An easing and nothing else; a shorthand that times a transition with one is a time.
|
|
447
|
+
const [only] = values;
|
|
448
|
+
if (values.length === 1 && only?.kind === "function" && EASING_FUNCTIONS.has(only.name)) {
|
|
449
|
+
return "easing";
|
|
450
|
+
}
|
|
451
|
+
if (/font/.test(name)) {
|
|
452
|
+
return "font";
|
|
453
|
+
}
|
|
454
|
+
if (hasUnit(values, TIME_UNITS)) {
|
|
455
|
+
return "time";
|
|
456
|
+
}
|
|
457
|
+
if (hasUnit(values, RAW_UNITS)) {
|
|
458
|
+
return /size/.test(name) ? "size" : /radius/.test(name) ? "radius" : "space";
|
|
459
|
+
}
|
|
460
|
+
return undefined;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** The kind of token a declaration wants, or undefined when no token kind fits it. */
|
|
464
|
+
function wantedKind(property: string, value: string): TokenKind | undefined {
|
|
465
|
+
const values = parseCssValue(value);
|
|
466
|
+
if (takesColor(property) && isColorValue(values)) {
|
|
467
|
+
return "color";
|
|
468
|
+
}
|
|
469
|
+
// An easing written out wants an easing token; a duration beside it is already one.
|
|
470
|
+
if (hasEasing(values)) {
|
|
471
|
+
return "easing";
|
|
472
|
+
}
|
|
473
|
+
if (property === "font-family") {
|
|
474
|
+
return "font";
|
|
475
|
+
}
|
|
476
|
+
if (property === "font-size") {
|
|
477
|
+
return "size";
|
|
478
|
+
}
|
|
479
|
+
if (/radius$/.test(property)) {
|
|
480
|
+
return "radius";
|
|
481
|
+
}
|
|
482
|
+
if (/^(margin|padding)(-|$)|^(row-|column-)?gap$/.test(property)) {
|
|
483
|
+
return "space";
|
|
484
|
+
}
|
|
485
|
+
if (/^(transition|animation)(-|$)/.test(property)) {
|
|
486
|
+
return "time";
|
|
487
|
+
}
|
|
488
|
+
return undefined;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* The theme tokens that could replace a raw value, such as `use var(--accent) or var(--fg)`
|
|
493
|
+
* for a hex color; none when no token holds that kind of value.
|
|
494
|
+
*/
|
|
495
|
+
export function tokenSuggestion(
|
|
496
|
+
property: string,
|
|
497
|
+
value: string,
|
|
498
|
+
tokens: Array<{ name: string; value: string }>,
|
|
499
|
+
): string | undefined {
|
|
500
|
+
const kind = wantedKind(property, value);
|
|
501
|
+
const names = [
|
|
502
|
+
...new Set(
|
|
503
|
+
tokens
|
|
504
|
+
.filter((token) => kind && tokenKind(token.name, token.value) === kind)
|
|
505
|
+
.map((t) => t.name),
|
|
506
|
+
),
|
|
507
|
+
].sort();
|
|
508
|
+
if (names.length === 0) {
|
|
509
|
+
return undefined;
|
|
510
|
+
}
|
|
511
|
+
const shown = names.slice(0, MAX_HINT_TOKENS).map((name) => `var(${name})`);
|
|
512
|
+
if (names.length > MAX_HINT_TOKENS) {
|
|
513
|
+
shown.push("…");
|
|
514
|
+
}
|
|
515
|
+
const last = shown.pop();
|
|
516
|
+
if (shown.length === 0) {
|
|
517
|
+
return `use ${last}`;
|
|
518
|
+
}
|
|
519
|
+
return shown.length === 1 ? `use ${shown[0]} or ${last}` : `use ${shown.join(", ")}, or ${last}`;
|
|
520
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { cuesFromDeck, silentCues, unknownAsciiWords } from "../cue.ts";
|
|
2
|
+
import { deckPaths } from "../deck-paths.ts";
|
|
3
|
+
import { type Diagnostic, diag } from "../diagnostic.ts";
|
|
4
|
+
import { DekError } from "../error.ts";
|
|
5
|
+
import type { ProjectDeck } from "../resolve.ts";
|
|
6
|
+
import { hasVoice, loadVoiceDict, loadVoiceSettings, resolveBeatTiming } from "../voice.ts";
|
|
7
|
+
import type { LintContext } from "./context.ts";
|
|
8
|
+
|
|
9
|
+
/** DEK042: a beat that shows a list, code, or table but has nothing to say. */
|
|
10
|
+
export function silentCueDiagnostics(deck: ProjectDeck): Diagnostic[] {
|
|
11
|
+
return silentCues(deck.deck).map((cue) => {
|
|
12
|
+
const where =
|
|
13
|
+
cue.position.beatIndex > 0 ? `"${cue.slug}" beat ${cue.position.beatIndex}` : `"${cue.slug}"`;
|
|
14
|
+
return diag("DEK042", {
|
|
15
|
+
message: `no spoken paragraph in ${where}; lists, code, and tables are not synthesized`,
|
|
16
|
+
path: deck.scriptPath,
|
|
17
|
+
line: cue.line,
|
|
18
|
+
slug: cue.slug,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* DEK042, DEK043, DEK040, and DEK045, for a deck with voice/: a live-only deck is done without
|
|
25
|
+
* narration, so these say nothing about it. A settings file that does not read is DEK045, and
|
|
26
|
+
* only the checks that need it are skipped.
|
|
27
|
+
*/
|
|
28
|
+
export function voiceDiagnostics({ deck }: LintContext): Diagnostic[] {
|
|
29
|
+
if (!hasVoice(deck.dir)) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const paths = deckPaths(deck.dir);
|
|
33
|
+
const unreadable: Diagnostic[] = [];
|
|
34
|
+
const read = <T>(path: string, load: () => T): T | undefined => {
|
|
35
|
+
try {
|
|
36
|
+
return load();
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (!(error instanceof DekError)) {
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
unreadable.push(
|
|
42
|
+
diag("DEK045", {
|
|
43
|
+
message: error.message,
|
|
44
|
+
path: error.path ?? path,
|
|
45
|
+
...(error.line !== undefined ? { line: error.line } : {}),
|
|
46
|
+
...(error.hint !== undefined ? { hint: error.hint } : {}),
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const settings = read(paths.voiceToml, () => loadVoiceSettings(deck.dir));
|
|
53
|
+
const unmatched = (settings ? resolveBeatTiming(deck.deck, settings).unknown : []).map((key) =>
|
|
54
|
+
diag("DEK043", {
|
|
55
|
+
message: `voice.toml [beats."${key}"] matches no slide or beat`,
|
|
56
|
+
path: paths.voiceToml,
|
|
57
|
+
data: { key },
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
const dict = read(paths.voiceDict, () => loadVoiceDict(deck.dir));
|
|
61
|
+
const unknownWords =
|
|
62
|
+
dict === undefined
|
|
63
|
+
? []
|
|
64
|
+
: cuesFromDeck(deck.deck).flatMap((cue) =>
|
|
65
|
+
unknownAsciiWords(cue.paragraphs.join("\n"), dict).map((word) =>
|
|
66
|
+
diag("DEK040", {
|
|
67
|
+
message: `dictionary is missing English word: ${word} in "${cue.slug}"`,
|
|
68
|
+
path: deck.scriptPath,
|
|
69
|
+
line: cue.line,
|
|
70
|
+
slug: cue.slug,
|
|
71
|
+
data: { word },
|
|
72
|
+
}),
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
return [...unreadable, ...silentCueDiagnostics(deck), ...unmatched, ...unknownWords];
|
|
76
|
+
}
|