@tasker-systems/temper-design 0.0.0-stage → 0.6.3

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.
@@ -0,0 +1,444 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/tasker-systems/temper/packages/temper-design/contract/theme.schema.json",
4
+ "title": "temper theme",
5
+ "description": "A theme is data: values for every role in the contract. Components consume roles, never values; a theme that omits a role or invents one does not validate.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "kind",
10
+ "name",
11
+ "title",
12
+ "version",
13
+ "appearance",
14
+ "tokens"
15
+ ],
16
+ "properties": {
17
+ "$schema": {
18
+ "type": "string"
19
+ },
20
+ "kind": {
21
+ "const": "theme"
22
+ },
23
+ "name": {
24
+ "type": "string",
25
+ "pattern": "^[a-z][a-z0-9-]*$",
26
+ "description": "Stable identifier; also the value of data-theme."
27
+ },
28
+ "title": {
29
+ "type": "string",
30
+ "minLength": 1
31
+ },
32
+ "version": {
33
+ "type": "string",
34
+ "pattern": "^\\d+\\.\\d+\\.\\d+$"
35
+ },
36
+ "description": {
37
+ "type": "string"
38
+ },
39
+ "appearance": {
40
+ "enum": [
41
+ "dark",
42
+ "light"
43
+ ],
44
+ "description": "Sets color-scheme; selects this theme when the system appearance matches and the person follows it."
45
+ },
46
+ "counterpart": {
47
+ "type": "string",
48
+ "pattern": "^[a-z][a-z0-9-]*$",
49
+ "description": "The theme of the opposite appearance that pairs with this one."
50
+ },
51
+ "provenance": {
52
+ "type": "string",
53
+ "description": "Where the values came from."
54
+ },
55
+ "tokens": {
56
+ "type": "object",
57
+ "additionalProperties": false,
58
+ "required": [
59
+ "color",
60
+ "font",
61
+ "radius",
62
+ "motion",
63
+ "tracking"
64
+ ],
65
+ "properties": {
66
+ "color": {
67
+ "type": "object",
68
+ "additionalProperties": false,
69
+ "required": [
70
+ "ground",
71
+ "surface",
72
+ "surface-raised",
73
+ "overlay",
74
+ "text",
75
+ "text-muted",
76
+ "text-subtle",
77
+ "text-faint",
78
+ "rule",
79
+ "rule-strong",
80
+ "accent",
81
+ "accent-muted",
82
+ "accent-line",
83
+ "accent-line-soft",
84
+ "accent-wash",
85
+ "accent-hover",
86
+ "on-accent",
87
+ "focus",
88
+ "notice",
89
+ "notice-wash",
90
+ "success",
91
+ "success-wash",
92
+ "danger",
93
+ "danger-wash",
94
+ "pending",
95
+ "pending-wash",
96
+ "region-arriving",
97
+ "region-arriving-wash",
98
+ "region-empty",
99
+ "region-empty-wash",
100
+ "region-gave-up",
101
+ "region-gave-up-wash",
102
+ "region-failed",
103
+ "region-failed-wash",
104
+ "author-human",
105
+ "author-agent",
106
+ "cat-1",
107
+ "cat-2",
108
+ "cat-3",
109
+ "cat-4",
110
+ "cat-5",
111
+ "cat-6",
112
+ "cat-7",
113
+ "cat-8",
114
+ "cat-neutral",
115
+ "code-key",
116
+ "code-string",
117
+ "code-number",
118
+ "code-comment"
119
+ ],
120
+ "properties": {
121
+ "ground": {
122
+ "$ref": "#/$defs/color",
123
+ "description": "The page/window ground. Everything else is composited over it."
124
+ },
125
+ "surface": {
126
+ "$ref": "#/$defs/color",
127
+ "description": "Panels, cards, docked peeks \u2014 a lifted fill over ground."
128
+ },
129
+ "surface-raised": {
130
+ "$ref": "#/$defs/color",
131
+ "description": "Popovers, menus, the command palette \u2014 one step above surface."
132
+ },
133
+ "overlay": {
134
+ "$ref": "#/$defs/color",
135
+ "description": "Scrim behind a modal. Translucent."
136
+ },
137
+ "text": {
138
+ "$ref": "#/$defs/color",
139
+ "description": "Primary reading text. Floor: 7:1 on ground and surface."
140
+ },
141
+ "text-muted": {
142
+ "$ref": "#/$defs/color",
143
+ "description": "Secondary text \u2014 decks, body in dense chrome. Floor: 4.5:1."
144
+ },
145
+ "text-subtle": {
146
+ "$ref": "#/$defs/color",
147
+ "description": "Tertiary text \u2014 mono labels, metadata strips, counts. Floor: 4.5:1."
148
+ },
149
+ "text-faint": {
150
+ "$ref": "#/$defs/color",
151
+ "description": "Disabled and decorative only; never carries information. Floor: 2.5:1."
152
+ },
153
+ "rule": {
154
+ "$ref": "#/$defs/color",
155
+ "description": "Hairline dividers."
156
+ },
157
+ "rule-strong": {
158
+ "$ref": "#/$defs/color",
159
+ "description": "Dividers that must survive a busy surface; input outlines."
160
+ },
161
+ "accent": {
162
+ "$ref": "#/$defs/color",
163
+ "description": "The single accent: section labels, the rail, links, primary actions. Floor: 4.5:1."
164
+ },
165
+ "accent-muted": {
166
+ "$ref": "#/$defs/color",
167
+ "description": "Accent at rest where full accent would shout (secondary links, idle markers)."
168
+ },
169
+ "accent-line": {
170
+ "$ref": "#/$defs/color",
171
+ "description": "Accent borders on buttons and focused inputs."
172
+ },
173
+ "accent-line-soft": {
174
+ "$ref": "#/$defs/color",
175
+ "description": "The rail motif and soft accent borders."
176
+ },
177
+ "accent-wash": {
178
+ "$ref": "#/$defs/color",
179
+ "description": "Selected-row / active-chip fill."
180
+ },
181
+ "accent-hover": {
182
+ "$ref": "#/$defs/color",
183
+ "description": "Hover fill on accent affordances."
184
+ },
185
+ "on-accent": {
186
+ "$ref": "#/$defs/color",
187
+ "description": "Text on a solid accent fill. Floor: 4.5:1 on accent."
188
+ },
189
+ "focus": {
190
+ "$ref": "#/$defs/color",
191
+ "description": "Keyboard focus ring. Floor: 3:1 on ground."
192
+ },
193
+ "notice": {
194
+ "$ref": "#/$defs/color",
195
+ "description": "A condition that wants the reader's attention (not a failure). Floor: 4.5:1 on its wash."
196
+ },
197
+ "notice-wash": {
198
+ "$ref": "#/$defs/color",
199
+ "description": "Fill behind a notice."
200
+ },
201
+ "success": {
202
+ "$ref": "#/$defs/color",
203
+ "description": "A completed act confirmed. Floor: 4.5:1 on its wash."
204
+ },
205
+ "success-wash": {
206
+ "$ref": "#/$defs/color",
207
+ "description": "Fill behind a success note."
208
+ },
209
+ "danger": {
210
+ "$ref": "#/$defs/color",
211
+ "description": "Destructive affordances (delete, retract). Floor: 4.5:1 on its wash."
212
+ },
213
+ "danger-wash": {
214
+ "$ref": "#/$defs/color",
215
+ "description": "Fill behind a destructive confirmation."
216
+ },
217
+ "pending": {
218
+ "$ref": "#/$defs/color",
219
+ "description": "Pending state \u2014 proposals awaiting ratification, threads addressed to you. State, not alarm. Floor: 4.5:1 on its wash."
220
+ },
221
+ "pending-wash": {
222
+ "$ref": "#/$defs/color",
223
+ "description": "Fill behind pending items."
224
+ },
225
+ "region-arriving": {
226
+ "$ref": "#/$defs/color",
227
+ "description": "Region state: a read in flight. Floor: 4.5:1 on its wash."
228
+ },
229
+ "region-arriving-wash": {
230
+ "$ref": "#/$defs/color",
231
+ "description": "Fill behind an arriving region."
232
+ },
233
+ "region-empty": {
234
+ "$ref": "#/$defs/color",
235
+ "description": "Region state: the read succeeded and there is nothing. Floor: 4.5:1 on its wash."
236
+ },
237
+ "region-empty-wash": {
238
+ "$ref": "#/$defs/color",
239
+ "description": "Fill behind an empty region."
240
+ },
241
+ "region-gave-up": {
242
+ "$ref": "#/$defs/color",
243
+ "description": "Region state: the system stopped waiting. A hue nothing else uses. Floor: 4.5:1 on its wash."
244
+ },
245
+ "region-gave-up-wash": {
246
+ "$ref": "#/$defs/color",
247
+ "description": "Fill behind a gave-up region."
248
+ },
249
+ "region-failed": {
250
+ "$ref": "#/$defs/color",
251
+ "description": "Region state: the read failed. Floor: 4.5:1 on its wash."
252
+ },
253
+ "region-failed-wash": {
254
+ "$ref": "#/$defs/color",
255
+ "description": "Fill behind a failed region."
256
+ },
257
+ "author-human": {
258
+ "$ref": "#/$defs/color",
259
+ "description": "Authorship gutter mark for a person. Floor: 3:1 on ground."
260
+ },
261
+ "author-agent": {
262
+ "$ref": "#/$defs/color",
263
+ "description": "Authorship gutter mark for an agent acting on someone's behalf. Floor: 3:1 on ground."
264
+ },
265
+ "cat-1": {
266
+ "$ref": "#/$defs/color",
267
+ "description": "Categorical slot 1 of 8. Vocabularies — core's doc types and plugins' — bind their terms to slots; they never name colours. Floor: 4.5:1 on ground, surface and surface-raised."
268
+ },
269
+ "cat-2": {
270
+ "$ref": "#/$defs/color",
271
+ "description": "Categorical slot 2 of 8. Vocabularies — core's doc types and plugins' — bind their terms to slots; they never name colours. Floor: 4.5:1 on ground, surface and surface-raised."
272
+ },
273
+ "cat-3": {
274
+ "$ref": "#/$defs/color",
275
+ "description": "Categorical slot 3 of 8. Vocabularies — core's doc types and plugins' — bind their terms to slots; they never name colours. Floor: 4.5:1 on ground, surface and surface-raised."
276
+ },
277
+ "cat-4": {
278
+ "$ref": "#/$defs/color",
279
+ "description": "Categorical slot 4 of 8. Vocabularies — core's doc types and plugins' — bind their terms to slots; they never name colours. Floor: 4.5:1 on ground, surface and surface-raised."
280
+ },
281
+ "cat-5": {
282
+ "$ref": "#/$defs/color",
283
+ "description": "Categorical slot 5 of 8. Vocabularies — core's doc types and plugins' — bind their terms to slots; they never name colours. Floor: 4.5:1 on ground, surface and surface-raised."
284
+ },
285
+ "cat-6": {
286
+ "$ref": "#/$defs/color",
287
+ "description": "Categorical slot 6 of 8. Vocabularies — core's doc types and plugins' — bind their terms to slots; they never name colours. Floor: 4.5:1 on ground, surface and surface-raised."
288
+ },
289
+ "cat-7": {
290
+ "$ref": "#/$defs/color",
291
+ "description": "Categorical slot 7 of 8. Vocabularies — core's doc types and plugins' — bind their terms to slots; they never name colours. Floor: 4.5:1 on ground, surface and surface-raised."
292
+ },
293
+ "cat-8": {
294
+ "$ref": "#/$defs/color",
295
+ "description": "Categorical slot 8 of 8. Vocabularies — core's doc types and plugins' — bind their terms to slots; they never name colours. Floor: 4.5:1 on ground, surface and surface-raised."
296
+ },
297
+ "cat-neutral": {
298
+ "$ref": "#/$defs/color",
299
+ "description": "The categorical mark bound to no slot: a doc type no vocabulary binds, or a tint deliberately unnamed. Floor: 4.5:1 on ground, surface and surface-raised."
300
+ },
301
+ "code-key": {
302
+ "$ref": "#/$defs/color",
303
+ "description": "Syntax: keys, attributes, names. Floor: 4.5:1 on surface."
304
+ },
305
+ "code-string": {
306
+ "$ref": "#/$defs/color",
307
+ "description": "Syntax: strings. Floor: 4.5:1 on surface."
308
+ },
309
+ "code-number": {
310
+ "$ref": "#/$defs/color",
311
+ "description": "Syntax: numbers, literals, keywords. Floor: 4.5:1 on surface."
312
+ },
313
+ "code-comment": {
314
+ "$ref": "#/$defs/color",
315
+ "description": "Syntax: comments. Floor: 4.5:1 on surface."
316
+ }
317
+ }
318
+ },
319
+ "font": {
320
+ "type": "object",
321
+ "additionalProperties": false,
322
+ "required": [
323
+ "reading",
324
+ "doing",
325
+ "ui"
326
+ ],
327
+ "properties": {
328
+ "reading": {
329
+ "$ref": "#/$defs/fontStack",
330
+ "description": "Reading face \u2014 prose, headings, decks."
331
+ },
332
+ "doing": {
333
+ "$ref": "#/$defs/fontStack",
334
+ "description": "Doing face \u2014 labels, commands, code, metadata."
335
+ },
336
+ "ui": {
337
+ "$ref": "#/$defs/fontStack",
338
+ "description": "Dense UI face \u2014 grids, form controls."
339
+ }
340
+ }
341
+ },
342
+ "radius": {
343
+ "type": "object",
344
+ "additionalProperties": false,
345
+ "required": [
346
+ "chip",
347
+ "panel"
348
+ ],
349
+ "properties": {
350
+ "chip": {
351
+ "$ref": "#/$defs/length",
352
+ "description": "Chips, code blocks, inputs."
353
+ },
354
+ "panel": {
355
+ "$ref": "#/$defs/length",
356
+ "description": "Panels and cards. The editorial default is 0."
357
+ }
358
+ }
359
+ },
360
+ "motion": {
361
+ "type": "object",
362
+ "additionalProperties": false,
363
+ "required": [
364
+ "quick",
365
+ "settle",
366
+ "easing"
367
+ ],
368
+ "properties": {
369
+ "quick": {
370
+ "$ref": "#/$defs/duration",
371
+ "description": "Hover/colour transitions."
372
+ },
373
+ "settle": {
374
+ "$ref": "#/$defs/duration",
375
+ "description": "Panel slide-in, tier fades."
376
+ },
377
+ "easing": {
378
+ "type": "string",
379
+ "pattern": "^(ease|ease-in|ease-out|ease-in-out|linear|cubic-bezier\\([^)]*\\))$",
380
+ "description": "The one easing curve."
381
+ }
382
+ }
383
+ },
384
+ "tracking": {
385
+ "type": "object",
386
+ "additionalProperties": false,
387
+ "required": [
388
+ "label",
389
+ "strip",
390
+ "eyebrow",
391
+ "ui",
392
+ "mark"
393
+ ],
394
+ "properties": {
395
+ "label": {
396
+ "$ref": "#/$defs/em",
397
+ "description": "Section labels."
398
+ },
399
+ "strip": {
400
+ "$ref": "#/$defs/em",
401
+ "description": "Metadata strips."
402
+ },
403
+ "eyebrow": {
404
+ "$ref": "#/$defs/em",
405
+ "description": "Hero eyebrows."
406
+ },
407
+ "ui": {
408
+ "$ref": "#/$defs/em",
409
+ "description": "Buttons, nav."
410
+ },
411
+ "mark": {
412
+ "$ref": "#/$defs/em",
413
+ "description": "Wordmark."
414
+ }
415
+ }
416
+ }
417
+ }
418
+ }
419
+ },
420
+ "$defs": {
421
+ "color": {
422
+ "type": "string",
423
+ "pattern": "^(#[0-9a-fA-F]{6}|rgba\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*(0|1|0?\\.\\d+)\\s*\\))$",
424
+ "description": "#rrggbb or rgba(r, g, b, a). No named colours, no hsl \u2014 the contrast check reads these."
425
+ },
426
+ "fontStack": {
427
+ "type": "string",
428
+ "minLength": 1,
429
+ "description": "A CSS font-family list. Fonts are bundled by each surface, never fetched \u2014 a surface must be able to render offline."
430
+ },
431
+ "length": {
432
+ "type": "string",
433
+ "pattern": "^(0|\\d+(\\.\\d+)?(px|rem))$"
434
+ },
435
+ "duration": {
436
+ "type": "string",
437
+ "pattern": "^\\d+ms$"
438
+ },
439
+ "em": {
440
+ "type": "string",
441
+ "pattern": "^-?\\d*\\.?\\d+em$"
442
+ }
443
+ }
444
+ }
@@ -0,0 +1,191 @@
1
+ import { type SimulationNodeDatum } from 'd3-force';
2
+ export type GraphLayout = 'force' | 'radial';
3
+ export interface LayoutInput {
4
+ id: string;
5
+ /** In a radial layout, a core node settles at the centre and the rest ring it. */
6
+ core?: boolean;
7
+ }
8
+ export interface Placed extends SimulationNodeDatum {
9
+ id: string;
10
+ /** Distinct neighbours among the placed nodes. */
11
+ degree: number;
12
+ x: number;
13
+ y: number;
14
+ }
15
+ /**
16
+ * Every edge between one pair of nodes, drawn as one line. Edges are kept in the order they came,
17
+ * oriented from `source` to `target`: an edge that runs the other way is `reversed`.
18
+ */
19
+ export interface Joined<E> {
20
+ edges: {
21
+ edge: E;
22
+ reversed: boolean;
23
+ }[];
24
+ source: Placed;
25
+ target: Placed;
26
+ }
27
+ /** The space the simulation runs in. A surface sizes it to its canvas. */
28
+ export interface Plane {
29
+ width: number;
30
+ height: number;
31
+ }
32
+ export declare const DEFAULT_PLANE: Plane;
33
+ /** A mark's radius: more connected, a little larger, and never past a bound. */
34
+ export declare const nodeRadius: (degree: number) => number;
35
+ /**
36
+ * How many other carried nodes each node is joined to. Neighbours, not edges: two relations
37
+ * between the same pair are one line, and a node joined only to itself has none.
38
+ */
39
+ export declare function degrees(nodes: {
40
+ id: string;
41
+ }[], edges: {
42
+ source: string;
43
+ target: string;
44
+ }[]): Map<string, number>;
45
+ export interface Settled<E> {
46
+ nodes: Placed[];
47
+ edges: Joined<E>[];
48
+ selfJoined: E[];
49
+ unconnected: string[];
50
+ /** Whether the radial force ran: asked for, and the placed nodes held both core and ring. */
51
+ radial: boolean;
52
+ }
53
+ /** Settles the connected nodes; see the module header for the policy. */
54
+ export declare function settle<E extends {
55
+ source: string;
56
+ target: string;
57
+ }>(input: LayoutInput[], edges: E[], layout: GraphLayout, plane?: Plane): Settled<E>;
58
+ export interface Box {
59
+ x1: number;
60
+ y1: number;
61
+ x2: number;
62
+ y2: number;
63
+ }
64
+ /** Truncate to `max` characters (code points, so no character is split) with a trailing ellipsis. */
65
+ export declare const truncate: (text: string, max: number) => string;
66
+ export interface Caption {
67
+ id: string;
68
+ x: number;
69
+ y: number;
70
+ text: string;
71
+ }
72
+ export interface CaptionOptions {
73
+ /** How many captions to try to draw at all, most connected first. A surface's room. */
74
+ max: number;
75
+ /** Approximate advance width of one character at the render font size. */
76
+ charWidth: number;
77
+ lineHeight: number;
78
+ /** Longest caption, in characters, before the ellipsis. */
79
+ cap: number;
80
+ /** Clearance around each reserved box. */
81
+ padding: number;
82
+ }
83
+ export declare const CAPTION: CaptionOptions;
84
+ /**
85
+ * Which captions can be drawn without landing on another caption or on another node's mark.
86
+ * Greedy, most connected first, ties broken by code-unit order on id. A caption that would collide
87
+ * is dropped, not nudged: a label moved off its node reads as belonging to whatever it landed
88
+ * near. Every node is still drawn and still named elsewhere (a tooltip, a hover card, a list).
89
+ */
90
+ export declare function placeCaptions(nodes: {
91
+ id: string;
92
+ x: number;
93
+ y: number;
94
+ degree: number;
95
+ label: string;
96
+ }[], options?: Partial<CaptionOptions>): Caption[];
97
+ /**
98
+ * The drawing's frame: every mark and caption, with a margin. A surface that fits its viewBox to
99
+ * the drawing reads it; one with a fixed canvas does not need it. Captions are measured with the
100
+ * same options `placeCaptions` was given, so the frame fits what was placed.
101
+ */
102
+ export declare function frame(nodes: Placed[], captions: Caption[], { margin, plane, caption, }?: {
103
+ margin?: number;
104
+ plane?: Plane;
105
+ caption?: Partial<CaptionOptions>;
106
+ }): Box;
107
+ /** How far from its centre a line ends at a mark: just past the mark's own rim. */
108
+ export declare const rimOf: (degree: number) => number;
109
+ /**
110
+ * A pair's line, trimmed to the rims of its two marks, and an arrowhead at each end an edge
111
+ * points to. Heads are shapes, not SVG markers, so a drawing holds no ids and any number of
112
+ * graphs share a page. A line run centre to centre beneath its marks would end every head under
113
+ * the node it points at. `rims` overrides where each end stops, for a mark that is not a circle.
114
+ */
115
+ export declare function edgeGeometry(source: Placed, target: Placed, ends: {
116
+ atSource: boolean;
117
+ atTarget: boolean;
118
+ }, rims?: {
119
+ source: number;
120
+ target: number;
121
+ }): {
122
+ x1: number;
123
+ y1: number;
124
+ x2: number;
125
+ y2: number;
126
+ heads: string[];
127
+ };
128
+ export type EdgeKind = 'contains' | 'leads_to' | 'express' | 'near';
129
+ /**
130
+ * What the grammar reads off an edge: the relation's label, kind, polarity and weight. Every
131
+ * field is optional, because a read may not carry it; each absence draws a stated default.
132
+ */
133
+ export interface Answered {
134
+ /** The relation, as the corpus names it. */
135
+ label?: string | null;
136
+ edgeKind?: EdgeKind;
137
+ polarity?: 'forward' | 'inverse';
138
+ /**
139
+ * The relation's strength, drawn as the line's width. Absent draws the unweighted width —
140
+ * never a default of 1, which is a real weight.
141
+ */
142
+ weight?: number | null;
143
+ }
144
+ export type StrokeRole = 'structural' | 'derived' | 'contradicts';
145
+ /**
146
+ * Stroke for an edge whose weight is absent. A width cannot say "not reported" by itself, so a
147
+ * surface names the weight, or its absence, where it names the relation.
148
+ */
149
+ export declare const UNWEIGHTED_WIDTH = 1.4;
150
+ /**
151
+ * Weight → width over the range weights take: asserted at 1.0 by default and only lowered by
152
+ * decay, so (0, 1] spans 1 to 2.5. A weight outside it is held to the bounds, so a stray value
153
+ * cannot draw a line that dominates the drawing.
154
+ */
155
+ export declare const widthOf: (weight: number) => number;
156
+ /** How one edge draws: its role (painted by `contract/graph.css`), dash, width, and heads. */
157
+ export interface Stroke {
158
+ role: StrokeRole;
159
+ dash: string | null;
160
+ width: number;
161
+ atSource: boolean;
162
+ atTarget: boolean;
163
+ /**
164
+ * A cross at the midpoint: contradicts' channel beyond colour. Its role sits too close to the
165
+ * structural one in luminance for a colour-blind reader, and dash (kind) and width (weight) are
166
+ * taken, so it carries a shape of its own.
167
+ */
168
+ cross: boolean;
169
+ }
170
+ export declare function stroke(edge: Answered): Stroke;
171
+ /** Where a pair's arrowheads go: at each end some edge between them points to. */
172
+ export declare function pairEnds<E extends Answered>(edges: {
173
+ edge: E;
174
+ reversed: boolean;
175
+ }[]): {
176
+ atSource: boolean;
177
+ atTarget: boolean;
178
+ };
179
+ /**
180
+ * How a pair's line is drawn when its edges differ: a contradiction is never hidden behind a
181
+ * plainer relation, a line dashes only when every edge on it dashes — with the dash the first
182
+ * dashing edge names — and it is never thinner than the heaviest relation on it.
183
+ */
184
+ export declare function pairStroke<E extends Answered>(edges: {
185
+ edge: E;
186
+ }[]): {
187
+ role: StrokeRole;
188
+ dash: string | null;
189
+ width: number;
190
+ cross: boolean;
191
+ };