browser-debugger-cli 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +142 -79
  2. package/dist/commands/css.d.ts +13 -0
  3. package/dist/commands/css.js +53 -0
  4. package/dist/commands/dom/audit.d.ts +14 -0
  5. package/dist/commands/dom/audit.js +87 -0
  6. package/dist/commands/dom/formInteraction.js +36 -6
  7. package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
  8. package/dist/commands/dom/helpers/keyAttributes.js +6 -4
  9. package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
  10. package/dist/commands/dom/helpers/screenshot.js +158 -38
  11. package/dist/commands/dom/index.js +4 -1
  12. package/dist/commands/dom/screenshot.js +10 -6
  13. package/dist/commands/dom/wait.js +5 -3
  14. package/dist/commands/helpJson.js +1 -1
  15. package/dist/commands/optionBehaviors.js +21 -6
  16. package/dist/commands/page.js +7 -4
  17. package/dist/commands/peek.d.ts +7 -0
  18. package/dist/commands/peek.js +65 -23
  19. package/dist/commands/shared/optionTypes.d.ts +5 -1
  20. package/dist/commands/start.d.ts +13 -0
  21. package/dist/commands/start.js +19 -2
  22. package/dist/commands/tail.d.ts +7 -1
  23. package/dist/commands/tail.js +13 -62
  24. package/dist/commands.js +2 -0
  25. package/dist/daemon/session/commandRegistry.js +7 -1
  26. package/dist/daemon/session/plugins.js +4 -52
  27. package/dist/daemon.js +7986 -6848
  28. package/dist/errors/messages.d.ts +34 -4
  29. package/dist/errors/messages.js +68 -5
  30. package/dist/index.js +610 -186
  31. package/dist/ipc/client.d.ts +4 -0
  32. package/dist/ipc/client.js +8 -0
  33. package/dist/ipc/protocol/auditTypes.d.ts +129 -0
  34. package/dist/ipc/protocol/auditTypes.js +6 -0
  35. package/dist/ipc/protocol/commands.d.ts +23 -0
  36. package/dist/ipc/protocol/commands.js +2 -0
  37. package/dist/ipc/protocol/domTypes.d.ts +4 -0
  38. package/dist/ipc/protocol/inspectTypes.d.ts +71 -8
  39. package/dist/runtime/css/search.d.ts +39 -0
  40. package/dist/runtime/css/search.js +122 -0
  41. package/dist/runtime/dom/actionEffects.d.ts +4 -1
  42. package/dist/runtime/dom/actionEffects.js +8 -4
  43. package/dist/runtime/dom/audit.d.ts +19 -0
  44. package/dist/runtime/dom/audit.js +36 -0
  45. package/dist/runtime/dom/auditModel.d.ts +45 -0
  46. package/dist/runtime/dom/auditModel.js +215 -0
  47. package/dist/runtime/dom/auditScripts.d.ts +107 -0
  48. package/dist/runtime/dom/auditScripts.js +112 -0
  49. package/dist/runtime/dom/elementGeometry.d.ts +8 -2
  50. package/dist/runtime/dom/elementGeometry.js +24 -8
  51. package/dist/runtime/dom/elementInfo.d.ts +3 -2
  52. package/dist/runtime/dom/elementInfo.js +8 -2
  53. package/dist/runtime/dom/formFillHelpers/fill.js +2 -2
  54. package/dist/runtime/dom/inspect.d.ts +7 -0
  55. package/dist/runtime/dom/inspect.js +88 -23
  56. package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
  57. package/dist/runtime/dom/inspectAllStyles.js +89 -7
  58. package/dist/runtime/dom/inspectCascade.d.ts +19 -2
  59. package/dist/runtime/dom/inspectCascade.js +214 -44
  60. package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
  61. package/dist/runtime/dom/inspectCascadeModel.js +108 -34
  62. package/dist/runtime/dom/inspectHints.d.ts +26 -3
  63. package/dist/runtime/dom/inspectHints.js +125 -9
  64. package/dist/runtime/dom/inspectModel.d.ts +3 -0
  65. package/dist/runtime/dom/inspectModel.js +30 -7
  66. package/dist/runtime/dom/inspectPaintModel.d.ts +48 -22
  67. package/dist/runtime/dom/inspectPaintModel.js +180 -68
  68. package/dist/runtime/dom/inspectRules.d.ts +19 -0
  69. package/dist/runtime/dom/inspectRules.js +21 -5
  70. package/dist/runtime/dom/inspectScripts.d.ts +85 -12
  71. package/dist/runtime/dom/inspectScripts.js +314 -28
  72. package/dist/runtime/dom/inspectTree.js +10 -2
  73. package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
  74. package/dist/runtime/dom/inspectWhyModel.js +52 -10
  75. package/dist/runtime/dom/layout.js +31 -9
  76. package/dist/runtime/dom/reactEventHelpers.d.ts +7 -0
  77. package/dist/runtime/dom/reactEventHelpers.js +27 -9
  78. package/dist/runtime/page/emulation.d.ts +13 -4
  79. package/dist/runtime/page/emulation.js +69 -4
  80. package/dist/runtime/page/userAgent.d.ts +17 -0
  81. package/dist/runtime/page/userAgent.js +57 -0
  82. package/dist/types.d.ts +4 -0
  83. package/dist/ui/formatters/audit.d.ts +19 -0
  84. package/dist/ui/formatters/audit.js +106 -0
  85. package/dist/ui/formatters/dom.d.ts +1 -1
  86. package/dist/ui/formatters/dom.js +6 -3
  87. package/dist/ui/formatters/inspect.js +42 -15
  88. package/dist/ui/formatters/status.js +1 -1
  89. package/dist/ui/messages/commands.d.ts +44 -7
  90. package/dist/ui/messages/commands.js +83 -11
  91. package/dist/ui/messages/preview.d.ts +6 -0
  92. package/dist/ui/messages/preview.js +9 -1
  93. package/dist/utils/cssValues.js +36 -4
  94. package/dist/utils/decisionTrees.js +0 -5
  95. package/dist/utils/suggestions.d.ts +4 -2
  96. package/dist/utils/suggestions.js +7 -5
  97. package/dist/utils/taskMappings.js +1 -1
  98. package/package.json +3 -2
@@ -6,6 +6,8 @@
6
6
  */
7
7
  import type { Declaration, Resolution } from './inspectCascade.js';
8
8
  import type { StyleMap } from './inspectLayoutModel.js';
9
+ import type { InspectHint } from '../../ipc/protocol/inspectTypes.js';
10
+ import type { VariableSetter } from './inspectScripts.js';
9
11
  /** A declaration that has no effect */
10
12
  export interface CssHint {
11
13
  kind: 'inactive' | 'unset-variable' | 'not-inherited';
@@ -17,6 +19,10 @@ export interface CssHint {
17
19
  reason: string;
18
20
  /** What would make it work */
19
21
  fix: string;
22
+ /** The longhands of a shorthand that have no effect, when the others do (`margin-top`, `margin-bottom`) */
23
+ only?: string[];
24
+ /** The custom properties that are not set (`unset-variable`) */
25
+ variables?: string[];
20
26
  /** The declaration */
21
27
  declaration: Declaration;
22
28
  }
@@ -24,15 +30,19 @@ export interface CssHint {
24
30
  interface HintContext {
25
31
  style: StyleMap;
26
32
  parentStyle: StyleMap | undefined;
33
+ /** `display` as the winning declaration wrote it, when it differs from the computed one (blockified) */
34
+ declaredDisplay?: string | undefined;
27
35
  /** The element is replaced (img, input, video…) */
28
36
  replaced: boolean;
29
37
  /** The element is a form control (input, textarea, select, button) */
30
38
  formControl?: boolean;
31
39
  }
32
40
  /**
33
- * Declarations of the element that have no effect: every longhand a
34
- * declaration wins is inactive (`margin: 0 4px` on an inline element still
35
- * moves it sideways, `gap` on a multi-column block still spaces the columns).
41
+ * Declarations of the element that have no effect: the longhands a
42
+ * declaration wins that are inactive, unless they only restate a default.
43
+ * When only some of a `margin` shorthand's longhands are inactive
44
+ * (`margin: 8px 12px` on an inline element: the sides still move it), the
45
+ * hint names those; other partly inactive shorthands are not hinted.
36
46
  *
37
47
  * @param cascade - Resolved properties (winning declarations are checked)
38
48
  * @param ctx - Computed styles of the element and its parent
@@ -49,6 +59,19 @@ export declare function inactiveHints(cascade: Map<string, Resolution>, ctx: Hin
49
59
  * @returns Hints
50
60
  */
51
61
  export declare function undefinedVariableHints(cascade: Map<string, Resolution>, style: StyleMap): CssHint[];
62
+ /**
63
+ * Unset-variable hints told where the page does set the variable, when it
64
+ * does: only in a rule that does not match now (`.btn:hover`: expected in
65
+ * the other state), only in `@keyframes` (while the animation runs), or to
66
+ * `inherit`/`initial`/an empty value in a rule that matches (nothing above
67
+ * gives it a value). A set variable is not a typo, so the "did you mean"
68
+ * suggestion goes.
69
+ *
70
+ * @param hints - Hints of the result
71
+ * @param setters - Where each variable is set, by name ({@link VARIABLE_SETTERS_JS})
72
+ * @returns Hints
73
+ */
74
+ export declare function explainUnsetVariables(hints: readonly InspectHint[], setters: Readonly<Record<string, VariableSetter>>): InspectHint[];
52
75
  /**
53
76
  * A form control drawn in the browser's font while its parent uses another:
54
77
  * controls do not inherit the font unless told to (a common oversight).
@@ -4,12 +4,14 @@
4
4
  * checked against authored declarations only (never the browser's own
5
5
  * styles), and `var()` references to custom properties that are not set.
6
6
  */
7
+ import { isDefaultValue } from './inspectAllStyles.js';
7
8
  import { unsetVariables } from './inspectVariables.js';
8
9
  import { findSimilarNames } from '../../utils/suggestions.js';
9
10
  const display = (style) => style?.['display'] ?? 'inline';
10
11
  const isFlex = (value) => /(^|-)flex$/.test(value);
11
12
  const isGrid = (value) => /(^|-)grid$/.test(value);
12
13
  const isFlexOrGrid = (value) => isFlex(value) || isGrid(value);
14
+ const horizontal = (style) => (style['writing-mode'] ?? 'horizontal-tb') === 'horizontal-tb';
13
15
  const isMulticol = (style) => (style['column-count'] ?? 'auto') !== 'auto' || (style['column-width'] ?? 'auto') !== 'auto';
14
16
  /** The checks, in DevTools' wording */
15
17
  const RULES = [
@@ -120,7 +122,7 @@ const RULES = [
120
122
  },
121
123
  {
122
124
  properties: ['margin-top', 'margin-bottom'],
123
- inactive: ({ style, replaced }) => display(style) === 'inline' && !replaced
125
+ inactive: ({ style, replaced }) => display(style) === 'inline' && !replaced && horizontal(style)
124
126
  ? {
125
127
  reason: 'display is inline (vertical margins do not move it)',
126
128
  fix: 'use display: inline-block or block',
@@ -129,10 +131,10 @@ const RULES = [
129
131
  },
130
132
  {
131
133
  properties: ['vertical-align'],
132
- inactive: ({ style }) => /^(inline|inline-block|inline-flex|inline-grid|table-cell)$/.test(display(style))
134
+ inactive: ({ style, parentStyle, declaredDisplay }) => /^(inline|table-cell|ruby)/.test(display(style))
133
135
  ? undefined
134
136
  : {
135
- reason: `display is ${display(style)}`,
137
+ reason: `display is ${display(style)}${blockified(declaredDisplay, style, parentStyle)}`,
136
138
  fix: 'vertical-align works on inline and table-cell boxes; use flex alignment instead',
137
139
  },
138
140
  },
@@ -165,9 +167,58 @@ const RULES = [
165
167
  },
166
168
  ];
167
169
  /**
168
- * Declarations of the element that have no effect: every longhand a
169
- * declaration wins is inactive (`margin: 0 4px` on an inline element still
170
- * moves it sideways, `gap` on a multi-column block still spaces the columns).
170
+ * Why a declared inline-level display computes to a block-level one: the
171
+ * element is a flex or grid item, floated or positioned (CSS blockification).
172
+ *
173
+ * @param declared - `display` as written, when it differs from the computed one
174
+ * @param style - Computed styles
175
+ * @param parentStyle - Computed styles of the layout parent
176
+ * @returns ` (inline-flex blockified: a flex item)`, or empty
177
+ */
178
+ function blockified(declared, style, parentStyle) {
179
+ if (!declared || declared === display(style) || !/^(inline|table-|ruby)/.test(declared))
180
+ return '';
181
+ const parent = display(parentStyle);
182
+ const cause = isFlex(parent)
183
+ ? 'a flex item'
184
+ : isGrid(parent)
185
+ ? 'a grid item'
186
+ : (style['float'] ?? 'none') !== 'none'
187
+ ? 'floated'
188
+ : /^(absolute|fixed)$/.test(style['position'] ?? '')
189
+ ? 'positioned'
190
+ : 'by its context';
191
+ return ` (${declared} blockified: ${cause})`;
192
+ }
193
+ /**
194
+ * Shorthands whose inactive longhands are worth a hint while the others
195
+ * work: vertical margins of an inline element are a common mistake, while
196
+ * `gap` in a multi-column block or `grid-template-areas: none` are not
197
+ */
198
+ const PARTIAL_SHORTHANDS = new Set(['margin', 'margin-block']);
199
+ /** Values that change nothing wherever they are written */
200
+ const NO_OP_KEYWORDS = new Set(['initial', 'unset', 'revert', 'revert-layer']);
201
+ /**
202
+ * Whether a declared longhand value is its default, so writing it has no
203
+ * effect in any case (`vertical-align: baseline`, `margin-top: 0` from a
204
+ * reset): such declarations are not worth a hint.
205
+ *
206
+ * @param declaration - Declaration of a longhand
207
+ * @returns True for a default value
208
+ */
209
+ function isNoOp(declaration) {
210
+ const value = declaration.value.trim().toLowerCase();
211
+ if (NO_OP_KEYWORDS.has(value))
212
+ return true;
213
+ const zero = /^[+-]?0*\.?0+([a-z]+|%)?$/.test(value) ? '0px' : value;
214
+ return isDefaultValue(declaration.property, value) || isDefaultValue(declaration.property, zero);
215
+ }
216
+ /**
217
+ * Declarations of the element that have no effect: the longhands a
218
+ * declaration wins that are inactive, unless they only restate a default.
219
+ * When only some of a `margin` shorthand's longhands are inactive
220
+ * (`margin: 8px 12px` on an inline element: the sides still move it), the
221
+ * hint names those; other partly inactive shorthands are not hinted.
171
222
  *
172
223
  * @param cascade - Resolved properties (winning declarations are checked)
173
224
  * @param ctx - Computed styles of the element and its parent
@@ -176,16 +227,21 @@ const RULES = [
176
227
  export function inactiveHints(cascade, ctx) {
177
228
  return [...ownWinnersByDeclaration(cascade).values()].flatMap((winners) => {
178
229
  const checks = winners.map((declaration) => RULES.find((rule) => rule.properties.includes(declaration.property))?.inactive(ctx));
230
+ const inactive = winners.filter((declaration, i) => checks[i] && !isNoOp(declaration));
179
231
  const [first] = winners;
180
- const [inactive] = checks;
181
- if (!first || !inactive || checks.some((check) => !check))
232
+ const reason = checks.find(Boolean);
233
+ const partial = checks.some((check) => !check);
234
+ if (!first || !reason || inactive.length === 0)
235
+ return [];
236
+ if (partial && !PARTIAL_SHORTHANDS.has(first.via ?? ''))
182
237
  return [];
183
238
  return [
184
239
  {
185
240
  kind: 'inactive',
186
241
  property: first.via ?? first.property,
187
242
  value: first.written ?? first.value,
188
- ...inactive,
243
+ ...reason,
244
+ ...(partial && { only: inactive.map((declaration) => declaration.property) }),
189
245
  declaration: first,
190
246
  },
191
247
  ];
@@ -243,11 +299,71 @@ export function undefinedVariableHints(cascade, style) {
243
299
  value,
244
300
  reason: `${missing.join(', ')} is not set`,
245
301
  fix: variableFix(missing[0] ?? '', style),
302
+ variables: missing,
246
303
  declaration,
247
304
  });
248
305
  }
249
306
  return hints;
250
307
  }
308
+ /**
309
+ * Unset-variable hints told where the page does set the variable, when it
310
+ * does: only in a rule that does not match now (`.btn:hover`: expected in
311
+ * the other state), only in `@keyframes` (while the animation runs), or to
312
+ * `inherit`/`initial`/an empty value in a rule that matches (nothing above
313
+ * gives it a value). A set variable is not a typo, so the "did you mean"
314
+ * suggestion goes.
315
+ *
316
+ * @param hints - Hints of the result
317
+ * @param setters - Where each variable is set, by name ({@link VARIABLE_SETTERS_JS})
318
+ * @returns Hints
319
+ */
320
+ export function explainUnsetVariables(hints, setters) {
321
+ return hints.map((hint) => {
322
+ const variables = hint.variables ?? [];
323
+ const name = variables.find((variable) => setters[variable]);
324
+ const setter = name ? setters[name] : undefined;
325
+ if (!name || !setter)
326
+ return hint;
327
+ const explained = setterExplanation(name, setter);
328
+ if (variables.every((variable) => setters[variable]))
329
+ return { ...hint, ...explained };
330
+ return { ...hint, reason: `${hint.reason}; ${explained.reason}` };
331
+ });
332
+ }
333
+ /**
334
+ * Why a variable that the page sets is unset here, and the fix.
335
+ *
336
+ * @param name - Variable
337
+ * @param setter - Where it is set
338
+ * @returns Reason and fix
339
+ */
340
+ function setterExplanation(name, setter) {
341
+ const fallback = 'give var() a fallback';
342
+ if (setter.keyframes) {
343
+ return {
344
+ reason: `${name} is set only in @keyframes ${setter.keyframes} (while it runs)`,
345
+ fix: `${fallback} for when the animation is not running`,
346
+ };
347
+ }
348
+ if (setter.condition) {
349
+ return {
350
+ reason: `${name} is set only by ${setter.selector} under ${setter.condition}, which does not apply now`,
351
+ fix: `expected under other conditions; otherwise ${fallback}`,
352
+ };
353
+ }
354
+ if (setter.matches) {
355
+ return {
356
+ reason: `${name} is set to ${setter.value === '' ? 'an empty value' : setter.value} by ${setter.selector}, and nothing above gives it a value`,
357
+ fix: `set ${name} on an ancestor, or ${fallback}`,
358
+ };
359
+ }
360
+ return {
361
+ reason: setter.matches === null
362
+ ? `${name} is set only by ${setter.selector} (whether it applies here is not known)`
363
+ : `${name} is set only by ${setter.selector}, which does not match now`,
364
+ fix: `expected in that state; otherwise ${fallback}`,
365
+ };
366
+ }
251
367
  /**
252
368
  * How to fix a `var()` of an unset custom property, naming a similar one
253
369
  * that is set (a typo or a renamed token).
@@ -16,7 +16,10 @@ export interface InspectSources {
16
16
  raw: RawInspect;
17
17
  style: StyleMap;
18
18
  parentStyle?: StyleMap;
19
+ /** Computed styles of the descendant that draws the text, when it is not the element */
20
+ holderStyle?: StyleMap;
19
21
  pseudo: PseudoSource[];
22
+ /** Platform fonts of the text (of the descendant that draws it, when there is one) */
20
23
  fonts: PlatformFont[];
21
24
  /** Border box size from `DOM.getBoxModel`; absent when the element has no box */
22
25
  size?: {
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { allStyles } from './inspectAllStyles.js';
9
9
  import { buildBox, buildLayout } from './inspectLayoutModel.js';
10
- import { buildEffects, buildFills, buildFx, buildOutline, buildPseudo, buildRadius, buildState, buildStrokes, buildText, effectiveBackground, } from './inspectPaintModel.js';
10
+ import { buildEffects, buildFills, buildSvgPaint, buildFx, buildOutline, buildPseudo, buildRadius, buildState, buildStrokes, buildText, effectiveBackground, } from './inspectPaintModel.js';
11
11
  import { buildTree, rowText } from './inspectTree.js';
12
12
  import { hexColor, relativeLuminance } from '../../utils/color.js';
13
13
  import { normalizeCssValue, round1 } from '../../utils/cssValues.js';
@@ -50,6 +50,30 @@ export function visibilityOf(layout, rendered) {
50
50
  ...(layout.coverTransparent && { coverTransparent: true }),
51
51
  };
52
52
  }
53
+ /**
54
+ * The header rectangle: border box size (and the box it covers on screen
55
+ * when a transform turns it), and the page position, or the
56
+ * viewport position of an element fixed to the viewport (it or a container
57
+ * is `position: fixed`: its page position changes with the scroll).
58
+ *
59
+ * @param layout - Layout measurements
60
+ * @param size - Border box size
61
+ * @param style - Computed styles (transforms)
62
+ * @returns Rectangle
63
+ */
64
+ function headerRect(layout, size, style) {
65
+ const { width, height } = layout.bounds;
66
+ const transformed = ['transform', 'rotate', 'scale'].some((name) => (style[name] ?? 'none') !== 'none');
67
+ const turned = transformed && (Math.abs(width - size.w) > 1 || Math.abs(height - size.h) > 1);
68
+ const box = {
69
+ w: round1(size.w),
70
+ h: round1(size.h),
71
+ ...(turned && { screen: { w: width, h: height } }),
72
+ };
73
+ return layout.fixed
74
+ ? { x: layout.viewport.x, y: layout.viewport.y, ...box, in: 'viewport' }
75
+ : { x: layout.bounds.x, y: layout.bounds.y, ...box };
76
+ }
53
77
  /** Below this relative luminance a page background counts as dark */
54
78
  const DARK_LUMINANCE = 0.18;
55
79
  /**
@@ -72,10 +96,7 @@ function header(sources, request) {
72
96
  ...(content && { content }),
73
97
  ...(raw.placeholder && { placeholder: rowText(raw.placeholder) }),
74
98
  ...(raw.context && { context: raw.context }),
75
- ...(size &&
76
- layout && {
77
- rect: { x: layout.bounds.x, y: layout.bounds.y, w: round1(size.w), h: round1(size.h) },
78
- }),
99
+ ...(size && layout && { rect: headerRect(layout, size, sources.style) }),
79
100
  visibility: visibilityOf(layout, size !== undefined),
80
101
  ...(sources.colorScheme && { colorScheme: sources.colorScheme }),
81
102
  ...(sources.colorScheme === 'dark' && pageLooksDark(raw) && { theme: 'dark' }),
@@ -104,8 +125,9 @@ function pageLooksDark(raw) {
104
125
  */
105
126
  function groups(sources) {
106
127
  const { style, parentStyle, raw } = sources;
107
- const text = buildText(style, parentStyle, raw, sources.fonts);
128
+ const text = buildText({ style, parentStyle, holderStyle: sources.holderStyle }, raw, sources.fonts);
108
129
  const fills = buildFills(style);
130
+ const paint = buildSvgPaint(style, raw);
109
131
  const strokes = buildStrokes(style);
110
132
  const radius = buildRadius(style);
111
133
  const outline = buildOutline(style);
@@ -120,6 +142,7 @@ function groups(sources) {
120
142
  layout: buildLayout(style, parentStyle, raw),
121
143
  ...(text && { text }),
122
144
  ...(fills.length > 0 && { fills }),
145
+ ...(paint && { paint }),
123
146
  ...(opacity !== 1 && { opacity }),
124
147
  ...(blend && blend !== 'normal' && { blend }),
125
148
  ...(strokes.length > 0 && { strokes }),
@@ -156,7 +179,7 @@ function treeFields(raw, limit) {
156
179
  * @returns Properties and values
157
180
  */
158
181
  function allFields(sources) {
159
- const all = allStyles(sources.style, sources.raw.svg === true);
182
+ const all = allStyles(sources.style, sources.raw.svg === true, sources.raw.formControl);
160
183
  for (const pseudo of sources.pseudo) {
161
184
  const content = pseudo.style['content'];
162
185
  if (content && content !== 'none' && content !== 'normal') {
@@ -7,7 +7,7 @@
7
7
  * backgrounds of the element and its ancestors composited over the page
8
8
  * canvas).
9
9
  */
10
- import type { InspectContrast, InspectEffect, InspectFill, InspectFx, InspectPseudo, InspectState, InspectStroke, InspectText, Sides } from '../../ipc/protocol/inspectTypes.js';
10
+ import type { InspectContrast, InspectEffect, InspectFill, InspectFx, InspectPseudo, InspectState, InspectStroke, InspectSvgPaint, InspectText, Sides } from '../../ipc/protocol/inspectTypes.js';
11
11
  import type { StyleMap } from './inspectLayoutModel.js';
12
12
  import type { RawBackground, RawInspect } from './inspectScripts.js';
13
13
  import { type Rgba } from '../../utils/color.js';
@@ -18,20 +18,24 @@ export interface PlatformFont {
18
18
  glyphCount: number;
19
19
  }
20
20
  /**
21
- * The font the text was rendered with, when it is not a face of the first
22
- * family (a fallback: `DM Sans 9pt` is a face of `DM Sans`, `Liberation Sans`
23
- * is not one of `Arial`), and whether it is a web font. Names
24
- * that are not readable (sites that scramble a web font's internal name)
25
- * are left out.
21
+ * The font the text was rendered with and whether it is a web font. For a
22
+ * generic first family, the font it resolved to (`resolved`). Otherwise
23
+ * `rendered` only when it is a fallback: not a face of the first family
24
+ * (`DM Sans 9pt` is a face of `DM Sans`, `Liberation Sans` is not one of
25
+ * `Arial`), and the first family is not a web font the page loaded (whose
26
+ * file may give any internal name: `Copyright Klim Type Foundry`, or the
27
+ * local font a `src: local()` points at). Names that are not readable
28
+ * (sites that scramble a web font's internal name) are left out.
26
29
  *
27
30
  * @param family - First family of `font-family`
28
31
  * @param fonts - Platform fonts of the text
29
- * @returns `rendered` and `webfont` fields
32
+ * @param familyLoaded - The first family is a loaded web font
33
+ * @returns `rendered`, `resolved` and `webfont` fields
30
34
  */
31
- export declare function renderedFont(family: string, fonts: readonly PlatformFont[]): Pick<InspectText, 'rendered' | 'webfont'>;
35
+ export declare function renderedFont(family: string, fonts: readonly PlatformFont[], familyLoaded?: boolean): Pick<InspectText, 'rendered' | 'resolved' | 'webfont'>;
32
36
  /**
33
37
  * The background behind the element's text: its own and its ancestors'
34
- * backgrounds composited, from the nearest opaque one (or the page canvas).
38
+ * backgrounds composited with their opacity over the page canvas.
35
39
  *
36
40
  * @param backgrounds - Backgrounds, the element's own first
37
41
  * @param canvasDark - The page canvas is dark
@@ -43,35 +47,57 @@ export declare function effectiveBackground(backgrounds: readonly RawBackground[
43
47
  overImage: boolean;
44
48
  };
45
49
  /**
46
- * Contrast of the text color with the background behind it.
50
+ * Contrast of the text color with the background behind it, both painted
51
+ * as the browser composites them ({@link paintOver}), with what makes the
52
+ * number approximate (blend modes, filters, content behind or on top).
47
53
  *
48
54
  * @param style - Computed styles (color, font size and weight)
49
- * @param raw - Backgrounds and the page canvas
55
+ * @param raw - Backgrounds, the page canvas, opacity and paint risks
50
56
  * @returns Ratio (rounded down to 2 decimals), level and background
51
57
  */
52
- export declare function textContrast(style: StyleMap, raw: Pick<RawInspect, 'backgrounds' | 'canvasDark' | 'opacity'>): InspectContrast | undefined;
58
+ export declare function textContrast(style: StyleMap, raw: Pick<RawInspect, 'backgrounds' | 'canvasDark' | 'opacity' | 'paintRisks'>): InspectContrast | undefined;
59
+ /** Computed styles the text group reads */
60
+ export interface TextStyles {
61
+ /** The element's */
62
+ style: StyleMap;
63
+ /** Its layout parent's */
64
+ parentStyle?: StyleMap | undefined;
65
+ /** The descendant's that draws most of its text, when it is not the element */
66
+ holderStyle?: StyleMap | undefined;
67
+ }
53
68
  /**
54
- * The text group. Elements with text (or form controls) get the full group:
55
- * font with the rendered font, size, color, alignment, contrast (not for
56
- * text no one can see: `opacity: 0` on it or an ancestor,
57
- * `visibility: hidden`) and the non-default extras. Containers get only what differs from
58
- * their parent, with the font their text was rendered in when the family
59
- * differs.
69
+ * The text group. Elements with text (or text fields) get the full group,
70
+ * read from whatever draws the text (the element, or the descendant with
71
+ * most of it, named in `holder`): font with the rendered font, size, color,
72
+ * alignment, contrast (not for text no one can see: not rendered,
73
+ * `opacity: 0` on it or an ancestor, `visibility: hidden`) and the
74
+ * non-default extras. Containers get only what differs from their parent,
75
+ * with the font their text was rendered in when that text has the
76
+ * container's family. Nothing for an element without text (an icon button,
77
+ * a checkbox).
60
78
  *
61
- * @param style - Computed styles
62
- * @param parentStyle - Computed styles of the layout parent
79
+ * @param styles - Computed styles of the element, its parent and its text holder
63
80
  * @param raw - Page-side measurements
64
81
  * @param fonts - Platform fonts of the text
65
82
  * @returns Text group, or undefined when there is nothing to say
66
83
  */
67
- export declare function buildText(style: StyleMap, parentStyle: StyleMap | undefined, raw: Pick<RawInspect, 'textual' | 'backgrounds' | 'canvasDark' | 'opacity'>, fonts: readonly PlatformFont[]): InspectText | undefined;
84
+ export declare function buildText(styles: TextStyles, raw: Pick<RawInspect, 'textual' | 'textHolder' | 'rendered' | 'hasText' | 'familyLoaded' | 'truncated' | 'backgrounds' | 'canvasDark' | 'opacity' | 'paintRisks'>, fonts: readonly PlatformFont[]): InspectText | undefined;
68
85
  /**
69
- * Background layers: images and gradients (top first), then the color.
86
+ * Background layers: images and gradients (top first), each with its own
87
+ * size and position when they are not the defaults, then the color.
70
88
  *
71
89
  * @param style - Computed styles
72
90
  * @returns Fills
73
91
  */
74
92
  export declare function buildFills(style: StyleMap): InspectFill[];
93
+ /**
94
+ * How an SVG element is painted: its `fill` and `stroke` (with the width).
95
+ *
96
+ * @param style - Computed styles
97
+ * @param raw - Whether it is an SVG element
98
+ * @returns Paint, or undefined for an HTML element
99
+ */
100
+ export declare function buildSvgPaint(style: StyleMap, raw: Pick<RawInspect, 'svg'>): InspectSvgPaint | undefined;
75
101
  /**
76
102
  * Border sides that show (a style other than none/hidden and a width): one
77
103
  * `all` stroke when the four are equal.