@formicoidea/labre-core 0.39.3 → 0.40.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/dist/_pkgs/affine-block-callout/emoji-margin.js +19 -11
- package/dist/_pkgs/affine-block-note/configs/tooltips.d.ts +16 -0
- package/dist/_pkgs/affine-block-note/configs/tooltips.js +99 -66
- package/dist/_pkgs/affine-block-paragraph/styles.js +30 -83
- package/dist/_pkgs/affine-block-paragraph/turbo/paragraph-layout-handler.js +1 -0
- package/dist/_pkgs/affine-block-paragraph/turbo/paragraph-painter.worker.d.ts +2 -0
- package/dist/_pkgs/affine-block-paragraph/turbo/paragraph-painter.worker.js +6 -1
- package/dist/_pkgs/affine-block-surface/framework-background/def.d.ts +4 -1
- package/dist/_pkgs/affine-block-surface/framework-background/def.js +6 -2
- package/dist/_pkgs/affine-gfx-template/builtin/other.js +31 -14
- package/dist/_pkgs/affine-inline-preset/markdown.d.ts +3 -0
- package/dist/_pkgs/affine-inline-preset/markdown.js +19 -0
- package/dist/_pkgs/affine-shared/consts/heading.d.ts +29 -0
- package/dist/_pkgs/affine-shared/consts/heading.js +70 -0
- package/dist/_pkgs/affine-shared/consts/index.d.ts +2 -0
- package/dist/_pkgs/affine-shared/consts/index.js +2 -0
- package/dist/_pkgs/affine-shared/consts/notation.d.ts +67 -0
- package/dist/_pkgs/affine-shared/consts/notation.js +65 -0
- package/package.json +1 -1
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
import { ParagraphBlockModel, } from '../affine-model/index.js';
|
|
2
|
+
import { HEADING_LEVELS, HEADING_SCALE, headingLineBox, } from '../affine-shared/consts/index.js';
|
|
2
3
|
import { matchModels } from '../affine-shared/utils/index.js';
|
|
4
|
+
/** The emoji's box, px (`.affine-callout-emoji-container` in callout-block.ts). */
|
|
5
|
+
const EMOJI_BOX_PX = 24;
|
|
3
6
|
/**
|
|
4
|
-
*
|
|
7
|
+
* H1's bold caps read as sitting low when the emoji is centred on the line
|
|
8
|
+
* box mathematically; lifting the emoji by 1px levels it optically.
|
|
9
|
+
*/
|
|
10
|
+
const H1_OPTICAL_LIFT_PX = 1;
|
|
11
|
+
/**
|
|
12
|
+
* How far the emoji has to drop to sit level with the first line.
|
|
5
13
|
*
|
|
6
14
|
* The emoji is a fixed 24px box next to a column whose first line can be
|
|
7
15
|
* anything from an H1 to an ordinary paragraph. A single margin therefore
|
|
8
16
|
* cannot be right twice: level with a paragraph it floats above an H1, level
|
|
9
|
-
* with an H1 it sinks below a paragraph.
|
|
10
|
-
* heading
|
|
17
|
+
* with an H1 it sinks below a paragraph. Per heading level, the drop centres
|
|
18
|
+
* the emoji on the heading's first line box, below its margin-top:
|
|
19
|
+
* `marginTop + lineBox / 2 - emojiBox / 2`.
|
|
11
20
|
*/
|
|
12
|
-
const EMOJI_MARGIN_TOP_BY_TYPE = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
21
|
+
const EMOJI_MARGIN_TOP_BY_TYPE = Object.fromEntries(HEADING_LEVELS.map(level => {
|
|
22
|
+
const centred = HEADING_SCALE[level].marginTop +
|
|
23
|
+
headingLineBox(level) / 2 -
|
|
24
|
+
EMOJI_BOX_PX / 2;
|
|
25
|
+
const drop = level === 'h1' ? centred - H1_OPTICAL_LIFT_PX : centred;
|
|
26
|
+
return [level, `${drop}px`];
|
|
27
|
+
}));
|
|
20
28
|
/** What a paragraph, a list, or an empty callout gets. */
|
|
21
29
|
const DEFAULT_EMOJI_MARGIN_TOP = '10px';
|
|
22
30
|
/**
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
+
import { type HeadingLevel } from '../../affine-shared/consts/index.js';
|
|
1
2
|
import type { SlashMenuTooltip } from '../../affine-widget-slash-menu/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* The body lines of a heading preview that fit ENTIRELY in the frame, with the
|
|
5
|
+
* baseline each is set on.
|
|
6
|
+
*
|
|
7
|
+
* The body starts right under the heading's line box, so how many of its lines
|
|
8
|
+
* the card can show depends on the level: derived here from the geometry (line
|
|
9
|
+
* boxes, frame height, bottom padding) rather than tuned per level. At 10px on
|
|
10
|
+
* a 12px line, Inter's descenders end within a twentieth of a pixel of the line
|
|
11
|
+
* box's bottom edge, which the bottom padding covers: a line whose box fits is
|
|
12
|
+
* never clipped.
|
|
13
|
+
*/
|
|
14
|
+
export declare function headingPreviewBodyLines(level: HeadingLevel): {
|
|
15
|
+
y: number;
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
2
18
|
export declare const BoldTextTooltip: import("lit-html").TemplateResult<1>;
|
|
3
19
|
export declare const ItalicTooltip: import("lit-html").TemplateResult<1>;
|
|
4
20
|
export declare const StrikethroughTooltip: import("lit-html").TemplateResult<1>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HEADING_SCALE, headingLineBox, } from '../../affine-shared/consts/index.js';
|
|
2
|
+
import { html, svg } from 'lit';
|
|
2
3
|
// prettier-ignore
|
|
3
4
|
const TextTooltip = html `<svg width="170" height="68" viewBox="0 0 170 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
4
5
|
<rect width="170" height="68" rx="2" fill="white"/>
|
|
@@ -10,78 +11,110 @@ const TextTooltip = html `<svg width="170" height="68" viewBox="0 0 170 68" fill
|
|
|
10
11
|
</g>
|
|
11
12
|
</svg>
|
|
12
13
|
`;
|
|
14
|
+
/** The preview frame: every slash-menu figure is a 170×68 card. */
|
|
15
|
+
const PREVIEW_HEIGHT = 68;
|
|
16
|
+
/** Space above the first line of a preview, px. */
|
|
17
|
+
const PREVIEW_PADDING_TOP = 6;
|
|
18
|
+
/**
|
|
19
|
+
* Space a preview keeps free under its last line, px. A body line is drawn only
|
|
20
|
+
* if its whole line box ends above it, so no line is ever cut by the frame.
|
|
21
|
+
*/
|
|
22
|
+
const PREVIEW_PADDING_BOTTOM = 2;
|
|
23
|
+
/** The body text under a heading preview, shrunk to 10px on a 12px line. */
|
|
24
|
+
const BODY_FONT_SIZE = 10;
|
|
25
|
+
const BODY_LINE_HEIGHT = 12;
|
|
26
|
+
/** Extra space between two paragraphs of the body, px. */
|
|
27
|
+
const BODY_PARAGRAPH_GAP = 4;
|
|
28
|
+
/**
|
|
29
|
+
* The body, as the lines it is set in (the `text` preview's own wording). A
|
|
30
|
+
* paragraph ends with a newline, as the Figma export wrote it.
|
|
31
|
+
*/
|
|
32
|
+
const BODY_PARAGRAPHS = [
|
|
33
|
+
[
|
|
34
|
+
'In a decentralized system, we can have a kaleidoscopic ',
|
|
35
|
+
'complexity to our data.\n',
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
'Any user may have a different perspective on what data they ',
|
|
39
|
+
'either have, choose to share, or accept.\n',
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
'For example, one user\u2019s edits to a document might be on ',
|
|
43
|
+
'their laptop on an airplane; when the plane lands and the ',
|
|
44
|
+
'computer reconnects, those changes are distributed to ',
|
|
45
|
+
'other users.\n',
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
'Other users might choose to accept all, some, or none of ',
|
|
49
|
+
'those changes to their version of the document.',
|
|
50
|
+
],
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* Where Figma sets an Inter baseline in a line box: 4/11 em below the box's
|
|
54
|
+
* centre (half of Inter's ascent minus descent).
|
|
55
|
+
*/
|
|
56
|
+
function interBaseline(top, lineHeight, fontSize) {
|
|
57
|
+
return Number((top + lineHeight / 2 + (fontSize * 4) / 11).toFixed(4));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The body lines of a heading preview that fit ENTIRELY in the frame, with the
|
|
61
|
+
* baseline each is set on.
|
|
62
|
+
*
|
|
63
|
+
* The body starts right under the heading's line box, so how many of its lines
|
|
64
|
+
* the card can show depends on the level: derived here from the geometry (line
|
|
65
|
+
* boxes, frame height, bottom padding) rather than tuned per level. At 10px on
|
|
66
|
+
* a 12px line, Inter's descenders end within a twentieth of a pixel of the line
|
|
67
|
+
* box's bottom edge, which the bottom padding covers: a line whose box fits is
|
|
68
|
+
* never clipped.
|
|
69
|
+
*/
|
|
70
|
+
export function headingPreviewBodyLines(level) {
|
|
71
|
+
const limit = PREVIEW_HEIGHT - PREVIEW_PADDING_BOTTOM;
|
|
72
|
+
const lines = [];
|
|
73
|
+
let top = PREVIEW_PADDING_TOP + headingLineBox(level);
|
|
74
|
+
for (const [index, paragraph] of BODY_PARAGRAPHS.entries()) {
|
|
75
|
+
if (index > 0)
|
|
76
|
+
top += BODY_PARAGRAPH_GAP;
|
|
77
|
+
for (const text of paragraph) {
|
|
78
|
+
if (top + BODY_LINE_HEIGHT > limit)
|
|
79
|
+
return lines;
|
|
80
|
+
lines.push({
|
|
81
|
+
y: interBaseline(top, BODY_LINE_HEIGHT, BODY_FONT_SIZE),
|
|
82
|
+
text,
|
|
83
|
+
});
|
|
84
|
+
top += BODY_LINE_HEIGHT;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return lines;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A heading preview drawn at the heading's real size, weight and line box
|
|
91
|
+
* (from `HEADING_SCALE`), above as many lines of body text as the card shows
|
|
92
|
+
* whole (`headingPreviewBodyLines`).
|
|
93
|
+
*/
|
|
13
94
|
// prettier-ignore
|
|
14
|
-
|
|
95
|
+
function headingTooltip(level) {
|
|
96
|
+
const { fontSize, fontWeight, letterSpacing } = HEADING_SCALE[level];
|
|
97
|
+
const lineBox = headingLineBox(level);
|
|
98
|
+
const maskId = `mask_heading_tooltip_${level}`;
|
|
99
|
+
const body = headingPreviewBodyLines(level).map(line => svg `<tspan x="8" y=${line.y}>${line.text}</tspan>`);
|
|
100
|
+
return html `<svg width="170" height="68" viewBox="0 0 170 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
15
101
|
<rect width="170" height="68" rx="2" fill="white"/>
|
|
16
|
-
<mask id
|
|
102
|
+
<mask id=${maskId} style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="170" height="68">
|
|
17
103
|
<rect width="170" height="68" rx="2" fill="white"/>
|
|
18
104
|
</mask>
|
|
19
|
-
<g mask="url(
|
|
20
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size
|
|
21
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size
|
|
22
|
-
</g>
|
|
23
|
-
</svg>
|
|
24
|
-
`;
|
|
25
|
-
// prettier-ignore
|
|
26
|
-
const Heading2Tooltip = html `<svg width="170" height="68" viewBox="0 0 170 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
27
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
28
|
-
<mask id="mask0_16460_880" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="170" height="68">
|
|
29
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
30
|
-
</mask>
|
|
31
|
-
<g mask="url(#mask0_16460_880)">
|
|
32
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="26" font-weight="600" letter-spacing="-0.24px"><tspan x="8" y="33.4545">Heading 2</tspan></text>
|
|
33
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0px"><tspan x="8" y="51.6364">In a decentralized system, we can have a kaleidoscopic </tspan><tspan x="8" y="63.6364">complexity to our data. </tspan><tspan x="8" y="79.6364">Any user may have a different perspective on what data they </tspan><tspan x="8" y="91.6364">either have, choose to share, or accept. </tspan><tspan x="8" y="107.636">For example, one user’s edits to a document might be on </tspan><tspan x="8" y="119.636">their laptop on an airplane; when the plane lands and the </tspan><tspan x="8" y="131.636">computer reconnects, those changes are distributed to </tspan><tspan x="8" y="143.636">other users. </tspan><tspan x="8" y="159.636">Other users might choose to accept all, some, or none of </tspan><tspan x="8" y="171.636">those changes to their version of the document.</tspan></text>
|
|
34
|
-
</g>
|
|
35
|
-
</svg>
|
|
36
|
-
`;
|
|
37
|
-
// prettier-ignore
|
|
38
|
-
const Heading3Tooltip = html `<svg width="170" height="68" viewBox="0 0 170 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
39
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
40
|
-
<mask id="mask0_16460_887" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="170" height="68">
|
|
41
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
42
|
-
</mask>
|
|
43
|
-
<g mask="url(#mask0_16460_887)">
|
|
44
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="24" font-weight="600" letter-spacing="-0.24px"><tspan x="8" y="30.7273">Heading 3</tspan></text>
|
|
45
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0px"><tspan x="8" y="47.6364">In a decentralized system, we can have a kaleidoscopic </tspan><tspan x="8" y="59.6364">complexity to our data. </tspan><tspan x="8" y="75.6364">Any user may have a different perspective on what data they </tspan><tspan x="8" y="87.6364">either have, choose to share, or accept. </tspan><tspan x="8" y="103.636">For example, one user’s edits to a document might be on </tspan><tspan x="8" y="115.636">their laptop on an airplane; when the plane lands and the </tspan><tspan x="8" y="127.636">computer reconnects, those changes are distributed to </tspan><tspan x="8" y="139.636">other users. </tspan><tspan x="8" y="155.636">Other users might choose to accept all, some, or none of </tspan><tspan x="8" y="167.636">those changes to their version of the document.</tspan></text>
|
|
46
|
-
</g>
|
|
47
|
-
</svg>
|
|
48
|
-
`;
|
|
49
|
-
// prettier-ignore
|
|
50
|
-
const Heading4Tooltip = html `<svg width="170" height="68" viewBox="0 0 170 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
51
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
52
|
-
<mask id="mask0_16460_894" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="170" height="68">
|
|
53
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
54
|
-
</mask>
|
|
55
|
-
<g mask="url(#mask0_16460_894)">
|
|
56
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="22" font-weight="600" letter-spacing="0.24px"><tspan x="8" y="29">Heading 4</tspan></text>
|
|
57
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0px"><tspan x="8" y="45.6364">In a decentralized system, we can have a kaleidoscopic </tspan><tspan x="8" y="57.6364">complexity to our data. </tspan><tspan x="8" y="73.6364">Any user may have a different perspective on what data they </tspan><tspan x="8" y="85.6364">either have, choose to share, or accept. </tspan><tspan x="8" y="101.636">For example, one user’s edits to a document might be on </tspan><tspan x="8" y="113.636">their laptop on an airplane; when the plane lands and the </tspan><tspan x="8" y="125.636">computer reconnects, those changes are distributed to </tspan><tspan x="8" y="137.636">other users. </tspan><tspan x="8" y="153.636">Other users might choose to accept all, some, or none of </tspan><tspan x="8" y="165.636">those changes to their version of the document.</tspan></text>
|
|
58
|
-
</g>
|
|
59
|
-
</svg>
|
|
60
|
-
`;
|
|
61
|
-
// prettier-ignore
|
|
62
|
-
const Heading5Tooltip = html `<svg width="170" height="68" viewBox="0 0 170 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
63
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
64
|
-
<mask id="mask0_16460_901" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="170" height="68">
|
|
65
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
66
|
-
</mask>
|
|
67
|
-
<g mask="url(#mask0_16460_901)">
|
|
68
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="20" font-weight="600" letter-spacing="0.24px"><tspan x="8" y="27.2727">Heading 5</tspan></text>
|
|
69
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0px"><tspan x="8" y="43.6364">In a decentralized system, we can have a kaleidoscopic </tspan><tspan x="8" y="55.6364">complexity to our data. </tspan><tspan x="8" y="71.6364">Any user may have a different perspective on what data they </tspan><tspan x="8" y="83.6364">either have, choose to share, or accept. </tspan><tspan x="8" y="99.6364">For example, one user’s edits to a document might be on </tspan><tspan x="8" y="111.636">their laptop on an airplane; when the plane lands and the </tspan><tspan x="8" y="123.636">computer reconnects, those changes are distributed to </tspan><tspan x="8" y="135.636">other users. </tspan><tspan x="8" y="151.636">Other users might choose to accept all, some, or none of </tspan><tspan x="8" y="163.636">those changes to their version of the document.</tspan></text>
|
|
70
|
-
</g>
|
|
71
|
-
</svg>
|
|
72
|
-
`;
|
|
73
|
-
// prettier-ignore
|
|
74
|
-
const Heading6Tooltip = html `<svg width="170" height="68" viewBox="0 0 170 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
75
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
76
|
-
<mask id="mask0_16460_908" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="170" height="68">
|
|
77
|
-
<rect width="170" height="68" rx="2" fill="white"/>
|
|
78
|
-
</mask>
|
|
79
|
-
<g mask="url(#mask0_16460_908)">
|
|
80
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="18" font-weight="600" letter-spacing="0.24px"><tspan x="8" y="25.5455">Heading 6</tspan></text>
|
|
81
|
-
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="10" letter-spacing="0px"><tspan x="8" y="41.6364">In a decentralized system, we can have a kaleidoscopic </tspan><tspan x="8" y="53.6364">complexity to our data. </tspan><tspan x="8" y="69.6364">Any user may have a different perspective on what data they </tspan><tspan x="8" y="81.6364">either have, choose to share, or accept. </tspan><tspan x="8" y="97.6364">For example, one user’s edits to a document might be on </tspan><tspan x="8" y="109.636">their laptop on an airplane; when the plane lands and the </tspan><tspan x="8" y="121.636">computer reconnects, those changes are distributed to </tspan><tspan x="8" y="133.636">other users. </tspan><tspan x="8" y="149.636">Other users might choose to accept all, some, or none of </tspan><tspan x="8" y="161.636">those changes to their version of the document.</tspan></text>
|
|
105
|
+
<g mask="url(#${maskId})">
|
|
106
|
+
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size=${fontSize} font-weight=${fontWeight} letter-spacing="${letterSpacing}em"><tspan x="8" y=${interBaseline(PREVIEW_PADDING_TOP, lineBox, fontSize)}>Heading ${level.slice(1)}</tspan></text>
|
|
107
|
+
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size=${BODY_FONT_SIZE} letter-spacing="0px">${body}</text>
|
|
82
108
|
</g>
|
|
83
109
|
</svg>
|
|
84
110
|
`;
|
|
111
|
+
}
|
|
112
|
+
const Heading1Tooltip = headingTooltip('h1');
|
|
113
|
+
const Heading2Tooltip = headingTooltip('h2');
|
|
114
|
+
const Heading3Tooltip = headingTooltip('h3');
|
|
115
|
+
const Heading4Tooltip = headingTooltip('h4');
|
|
116
|
+
const Heading5Tooltip = headingTooltip('h5');
|
|
117
|
+
const Heading6Tooltip = headingTooltip('h6');
|
|
85
118
|
// prettier-ignore
|
|
86
119
|
const CodeBlockTooltip = html `<svg width="170" height="68" viewBox="0 0 170 68" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
87
120
|
<rect width="170" height="68" rx="2" fill="white"/>
|
|
@@ -1,5 +1,32 @@
|
|
|
1
|
+
import { HEADING_LEVELS, HEADING_SCALE } from '../affine-shared/consts/index.js';
|
|
1
2
|
import { unsafeCSSVarV2 } from '../affine-shared/theme/index.js';
|
|
2
|
-
import { css } from 'lit';
|
|
3
|
+
import { css, unsafeCSS } from 'lit';
|
|
4
|
+
/** Inline code is set this much smaller than the text around it. */
|
|
5
|
+
const INLINE_CODE_SHRINK_PX = 3;
|
|
6
|
+
/**
|
|
7
|
+
* H1…H6, generated from `HEADING_SCALE` so every size, line box and margin has
|
|
8
|
+
* one source. Inline code in a heading stays `INLINE_CODE_SHRINK_PX` below the
|
|
9
|
+
* heading's size; H6, the level closest to body text, keeps the body's code
|
|
10
|
+
* padding (with its 2px bottom).
|
|
11
|
+
*/
|
|
12
|
+
const headingStyles = unsafeCSS(HEADING_LEVELS.map(level => {
|
|
13
|
+
const { fontSize, fontWeight, letterSpacing, lineHeightExtra, marginTop } = HEADING_SCALE[level];
|
|
14
|
+
return `
|
|
15
|
+
.${level} {
|
|
16
|
+
font-size: ${fontSize}px;
|
|
17
|
+
font-weight: ${fontWeight};
|
|
18
|
+
letter-spacing: ${letterSpacing}em;
|
|
19
|
+
line-height: calc(1em + ${lineHeightExtra}px);
|
|
20
|
+
margin-top: ${marginTop}px;
|
|
21
|
+
margin-bottom: 10px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.${level} code {
|
|
25
|
+
font-size: ${fontSize - INLINE_CODE_SHRINK_PX}px;
|
|
26
|
+
padding: ${level === 'h6' ? '0px 4px 2px' : '0px 4px'};
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
}).join(''));
|
|
3
30
|
export const paragraphBlockStyles = css `
|
|
4
31
|
affine-paragraph {
|
|
5
32
|
box-sizing: border-box;
|
|
@@ -21,91 +48,11 @@ export const paragraphBlockStyles = css `
|
|
|
21
48
|
}
|
|
22
49
|
|
|
23
50
|
affine-paragraph code {
|
|
24
|
-
font-size: calc(var(--affine-font-base) -
|
|
51
|
+
font-size: calc(var(--affine-font-base) - ${INLINE_CODE_SHRINK_PX}px);
|
|
25
52
|
padding: 0px 4px 2px;
|
|
26
53
|
}
|
|
27
54
|
|
|
28
|
-
|
|
29
|
-
font-size: var(--affine-font-h-1);
|
|
30
|
-
font-weight: 700;
|
|
31
|
-
letter-spacing: -0.02em;
|
|
32
|
-
line-height: calc(1em + 8px);
|
|
33
|
-
margin-top: 18px;
|
|
34
|
-
margin-bottom: 10px;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.h1 code {
|
|
38
|
-
font-size: calc(var(--affine-font-base) + 10px);
|
|
39
|
-
padding: 0px 4px;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.h2 {
|
|
43
|
-
font-size: var(--affine-font-h-2);
|
|
44
|
-
font-weight: 600;
|
|
45
|
-
letter-spacing: -0.02em;
|
|
46
|
-
line-height: calc(1em + 10px);
|
|
47
|
-
margin-top: 14px;
|
|
48
|
-
margin-bottom: 10px;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.h2 code {
|
|
52
|
-
font-size: calc(var(--affine-font-base) + 8px);
|
|
53
|
-
padding: 0px 4px;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.h3 {
|
|
57
|
-
font-size: var(--affine-font-h-3);
|
|
58
|
-
font-weight: 600;
|
|
59
|
-
letter-spacing: -0.02em;
|
|
60
|
-
line-height: calc(1em + 8px);
|
|
61
|
-
margin-top: 12px;
|
|
62
|
-
margin-bottom: 10px;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.h3 code {
|
|
66
|
-
font-size: calc(var(--affine-font-base) + 6px);
|
|
67
|
-
padding: 0px 4px;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.h4 {
|
|
71
|
-
font-size: var(--affine-font-h-4);
|
|
72
|
-
font-weight: 600;
|
|
73
|
-
letter-spacing: -0.015em;
|
|
74
|
-
line-height: calc(1em + 8px);
|
|
75
|
-
margin-top: 12px;
|
|
76
|
-
margin-bottom: 10px;
|
|
77
|
-
}
|
|
78
|
-
.h4 code {
|
|
79
|
-
font-size: calc(var(--affine-font-base) + 4px);
|
|
80
|
-
padding: 0px 4px;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.h5 {
|
|
84
|
-
font-size: var(--affine-font-h-5);
|
|
85
|
-
font-weight: 600;
|
|
86
|
-
letter-spacing: -0.015em;
|
|
87
|
-
line-height: calc(1em + 8px);
|
|
88
|
-
margin-top: 12px;
|
|
89
|
-
margin-bottom: 10px;
|
|
90
|
-
}
|
|
91
|
-
.h5 code {
|
|
92
|
-
font-size: calc(var(--affine-font-base) + 2px);
|
|
93
|
-
padding: 0px 4px;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.h6 {
|
|
97
|
-
font-size: var(--affine-font-h-6);
|
|
98
|
-
font-weight: 600;
|
|
99
|
-
letter-spacing: -0.015em;
|
|
100
|
-
line-height: calc(1em + 8px);
|
|
101
|
-
margin-top: 12px;
|
|
102
|
-
margin-bottom: 10px;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
.h6 code {
|
|
106
|
-
font-size: var(--affine-font-base);
|
|
107
|
-
padding: 0px 4px 2px;
|
|
108
|
-
}
|
|
55
|
+
${headingStyles}
|
|
109
56
|
|
|
110
57
|
.quote {
|
|
111
58
|
line-height: 26px;
|
|
@@ -19,6 +19,7 @@ export class ParagraphLayoutHandlerExtension extends BlockLayoutHandlerExtension
|
|
|
19
19
|
const { zoom, viewScale } = viewportRecord;
|
|
20
20
|
const paragraph = {
|
|
21
21
|
type: 'affine:paragraph',
|
|
22
|
+
paragraphType: model.props.type,
|
|
22
23
|
sentences: [],
|
|
23
24
|
blockId: model.id,
|
|
24
25
|
rect: { x: 0, y: 0, w: 0, h: 0 },
|
|
@@ -6,6 +6,8 @@ interface SentenceLayout {
|
|
|
6
6
|
}
|
|
7
7
|
export interface ParagraphLayout extends BlockLayout {
|
|
8
8
|
type: 'affine:paragraph';
|
|
9
|
+
/** The paragraph block's own `props.type`: `text`, `quote`, `h1`…`h6`. */
|
|
10
|
+
paragraphType: string;
|
|
9
11
|
sentences: SentenceLayout[];
|
|
10
12
|
}
|
|
11
13
|
export declare const ParagraphLayoutPainterExtension: import("../../store/index.js").ExtensionType;
|
|
@@ -39,12 +39,17 @@ class ParagraphLayoutPainter {
|
|
|
39
39
|
console.warn('Expected paragraph layout but received different format:', layout);
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
+
// Headings are not painted (TODO: fine-tune the baseline for heading
|
|
43
|
+
// sizes). Skipped on the block TYPE, not on the font size: H6 is set at
|
|
44
|
+
// the body's 15px (`HEADING_SCALE`), so a size test would paint it as text.
|
|
45
|
+
if (layout.paragraphType.startsWith('h'))
|
|
46
|
+
return;
|
|
42
47
|
const renderedPositions = new Set();
|
|
43
48
|
layout.sentences.forEach(sentence => {
|
|
44
49
|
const fontSize = sentence.fontSize;
|
|
45
50
|
const baselineY = getBaseline(fontSize);
|
|
46
51
|
if (fontSize !== 15)
|
|
47
|
-
return; //
|
|
52
|
+
return; // the baseline is tuned for body text only
|
|
48
53
|
ctx.font = `${fontSize}px Inter`;
|
|
49
54
|
ctx.strokeStyle = 'yellow';
|
|
50
55
|
sentence.rects.forEach(textRect => {
|
|
@@ -494,7 +494,10 @@ export interface FrameworkBackgroundDef {
|
|
|
494
494
|
transitionBandWidth?: Ratio;
|
|
495
495
|
chrome?: BackgroundChromeDef;
|
|
496
496
|
}
|
|
497
|
-
/**
|
|
497
|
+
/**
|
|
498
|
+
* The default card: a plain rectangle in the shared notation scale's card
|
|
499
|
+
* fill, no border, no radius.
|
|
500
|
+
*/
|
|
498
501
|
export declare const DEFAULT_BACKGROUND_SURFACE: BackgroundSurfaceDef;
|
|
499
502
|
/** The plot rectangle of a background of size `w × h`, in element-local units. */
|
|
500
503
|
export declare function backgroundPlot(def: FrameworkBackgroundDef, w: number, h: number): {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { NOTATION_NEUTRALS } from '../../affine-shared/consts/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The default card: a plain rectangle in the shared notation scale's card
|
|
4
|
+
* fill, no border, no radius.
|
|
5
|
+
*/
|
|
2
6
|
export const DEFAULT_BACKGROUND_SURFACE = {
|
|
3
|
-
fill:
|
|
7
|
+
fill: NOTATION_NEUTRALS.cardFill,
|
|
4
8
|
};
|
|
5
9
|
/** The plot rectangle of a background of size `w × h`, in element-local units. */
|
|
6
10
|
export function backgroundPlot(def, w, h) {
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { ConnectorMode, FontFamily, FontWeight, PointStyle, ShapeStyle, StrokeStyle, TextAlign, } from '../../affine-model/index.js';
|
|
2
|
+
import { NOTATION_NEUTRALS } from '../../affine-shared/consts/index.js';
|
|
2
3
|
import { makeTemplateSnapshot, surfaceText, } from '../make-snapshot.js';
|
|
3
4
|
/**
|
|
4
5
|
* The generic ("Other") diagrams: built ONLY from general BlockSuite shapes
|
|
5
6
|
* (`shape` rect/polygon, `text`, `connector`) per the composition principle —
|
|
6
7
|
* they belong to no framework, so they ship from the template package itself.
|
|
7
8
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Their neutrals are the shared notation scale's, like every framework's:
|
|
11
|
+
* the ink, the divider grey, the label grey and the card white. Only
|
|
12
|
+
* the hues (kanban cards, gantt bars) are the diagrams' own.
|
|
13
|
+
*/
|
|
14
|
+
const DARK = NOTATION_NEUTRALS.ink;
|
|
15
|
+
const MUTED = NOTATION_NEUTRALS.divider;
|
|
10
16
|
function rect(x, y, w, h, opts = {}) {
|
|
11
17
|
const el = {
|
|
12
18
|
type: 'shape',
|
|
13
19
|
shapeType: opts.shapeType ?? 'rect',
|
|
14
20
|
filled: true,
|
|
15
|
-
fillColor: opts.fill ??
|
|
21
|
+
fillColor: opts.fill ?? NOTATION_NEUTRALS.cardFill,
|
|
16
22
|
strokeColor: opts.stroke ?? DARK,
|
|
17
23
|
strokeWidth: opts.sw ?? 2,
|
|
18
24
|
strokeStyle: opts.dash ? StrokeStyle.Dash : StrokeStyle.Solid,
|
|
@@ -36,7 +42,7 @@ function label(x, y, w, h, str, opts = {}) {
|
|
|
36
42
|
return {
|
|
37
43
|
type: 'text',
|
|
38
44
|
text: surfaceText(str),
|
|
39
|
-
color: opts.color ??
|
|
45
|
+
color: opts.color ?? DARK,
|
|
40
46
|
fontFamily: FontFamily.Inter,
|
|
41
47
|
fontSize: opts.fontSize ?? 16,
|
|
42
48
|
fontWeight: opts.weight ?? FontWeight.Regular,
|
|
@@ -72,7 +78,12 @@ function swot() {
|
|
|
72
78
|
}
|
|
73
79
|
// ── Kanban (To do / Doing / Done) ─────────────────────────────────────
|
|
74
80
|
function kanban() {
|
|
75
|
-
const colOpts = {
|
|
81
|
+
const colOpts = {
|
|
82
|
+
fill: NOTATION_NEUTRALS.cardFill,
|
|
83
|
+
stroke: MUTED,
|
|
84
|
+
sw: 1.5,
|
|
85
|
+
radius: 10,
|
|
86
|
+
};
|
|
76
87
|
const head = {
|
|
77
88
|
fontSize: 18,
|
|
78
89
|
weight: FontWeight.Medium,
|
|
@@ -109,7 +120,10 @@ function bmc() {
|
|
|
109
120
|
});
|
|
110
121
|
const hdr = (x, s) => ({
|
|
111
122
|
box: rect(x, 8, 128, 34, { stroke: MUTED, sw: 1 }),
|
|
112
|
-
txt: label(x + 8, 16, 120, 18, s, {
|
|
123
|
+
txt: label(x + 8, 16, 120, 18, s, {
|
|
124
|
+
fontSize: 11,
|
|
125
|
+
color: NOTATION_NEUTRALS.label,
|
|
126
|
+
}),
|
|
113
127
|
});
|
|
114
128
|
const h1 = hdr(470, 'Designed for');
|
|
115
129
|
const h2 = hdr(602, 'Designed by');
|
|
@@ -156,7 +170,7 @@ function fishbone() {
|
|
|
156
170
|
sw: 1.4,
|
|
157
171
|
dash: true,
|
|
158
172
|
text: n,
|
|
159
|
-
textColor:
|
|
173
|
+
textColor: NOTATION_NEUTRALS.label,
|
|
160
174
|
fontSize: 13,
|
|
161
175
|
});
|
|
162
176
|
const out = {
|
|
@@ -190,10 +204,13 @@ function gantt() {
|
|
|
190
204
|
// week gridlines + labels
|
|
191
205
|
for (let i = 0; i < 6; i++) {
|
|
192
206
|
const x = 220 + i * 130;
|
|
193
|
-
out[`g${i}`] = line(x, 40, x, 268, {
|
|
207
|
+
out[`g${i}`] = line(x, 40, x, 268, {
|
|
208
|
+
stroke: NOTATION_NEUTRALS.cardBorder,
|
|
209
|
+
sw: 1,
|
|
210
|
+
});
|
|
194
211
|
out[`w${i}`] = label(x - 16, 12, 40, 20, `W${i + 1}`, {
|
|
195
212
|
fontSize: 12,
|
|
196
|
-
color:
|
|
213
|
+
color: NOTATION_NEUTRALS.label,
|
|
197
214
|
align: TextAlign.Center,
|
|
198
215
|
});
|
|
199
216
|
}
|
|
@@ -212,11 +229,11 @@ function gantt() {
|
|
|
212
229
|
}
|
|
213
230
|
const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
|
|
214
231
|
const previews = {
|
|
215
|
-
swot: `<svg ${ATTRS} fill="none"><rect x="20" y="12" width="95" height="56" stroke="
|
|
216
|
-
kanban: `<svg ${ATTRS} fill="none"><rect x="10" y="12" width="35" height="56" rx="4" stroke="
|
|
217
|
-
bmc: `<svg ${ATTRS} fill="none"><g stroke="
|
|
218
|
-
fishbone: `<svg ${ATTRS} fill="none"><path d="M14 40 H112" stroke="
|
|
219
|
-
gantt: `<svg ${ATTRS} fill="none"><path d="M40 14 V70 M62 14 V70 M84 14 V70 M106 14 V70" stroke="
|
|
232
|
+
swot: `<svg ${ATTRS} fill="none"><rect x="20" y="12" width="95" height="56" stroke="${DARK}" stroke-width="2"/><path d="M67.5 12 V68 M20 40 H115" stroke="${DARK}" stroke-width="1.6"/></svg>`,
|
|
233
|
+
kanban: `<svg ${ATTRS} fill="none"><rect x="10" y="12" width="35" height="56" rx="4" stroke="${MUTED}"/><rect x="50" y="12" width="35" height="56" rx="4" stroke="${MUTED}"/><rect x="90" y="12" width="35" height="56" rx="4" stroke="${MUTED}"/><rect x="15" y="22" width="25" height="11" rx="2" fill="#fde6c8"/><rect x="55" y="22" width="25" height="11" rx="2" fill="#d6e4fb"/><rect x="95" y="22" width="25" height="11" rx="2" fill="#d5efd9"/></svg>`,
|
|
234
|
+
bmc: `<svg ${ATTRS} fill="none"><g stroke="${DARK}" stroke-width="1.2"><rect x="8" y="14" width="22" height="40"/><rect x="32" y="14" width="22" height="20"/><rect x="32" y="35" width="22" height="19"/><rect x="56" y="14" width="22" height="40"/><rect x="80" y="14" width="22" height="20"/><rect x="80" y="35" width="22" height="19"/><rect x="104" y="14" width="22" height="40"/><rect x="8" y="56" width="57" height="14"/><rect x="68" y="56" width="58" height="14"/></g></svg>`,
|
|
235
|
+
fishbone: `<svg ${ATTRS} fill="none"><path d="M14 40 H112" stroke="${DARK}" stroke-width="3"/><rect x="112" y="33" width="20" height="14" stroke="${DARK}" stroke-width="1.4"/><path d="M40 18 L52 40 M40 62 L52 40 M84 18 L96 40 M84 62 L96 40" stroke="${DARK}" stroke-width="1.4"/></svg>`,
|
|
236
|
+
gantt: `<svg ${ATTRS} fill="none"><path d="M40 14 V70 M62 14 V70 M84 14 V70 M106 14 V70" stroke="${NOTATION_NEUTRALS.cardBorder}"/><rect x="40" y="22" width="34" height="8" rx="2" fill="#4574c4"/><rect x="52" y="36" width="44" height="8" rx="2" fill="#2f9e95"/><rect x="62" y="50" width="50" height="8" rx="2" fill="#d99a2b"/><rect x="84" y="64" width="28" height="8" rx="2" fill="#43a06b"/></svg>`,
|
|
220
237
|
};
|
|
221
238
|
function t(name, preview, elements) {
|
|
222
239
|
return {
|
|
@@ -18,4 +18,7 @@ export declare const UnderthroughExtension: ExtensionType & {
|
|
|
18
18
|
export declare const CodeExtension: ExtensionType & {
|
|
19
19
|
identifier: import("../global/di/types.js").ServiceIdentifier<import("../std/inline/index.js").InlineMarkdownMatch<AffineTextAttributes>>;
|
|
20
20
|
};
|
|
21
|
+
export declare const EmDashExtension: ExtensionType & {
|
|
22
|
+
identifier: import("../global/di/types.js").ServiceIdentifier<import("../std/inline/index.js").InlineMarkdownMatch<AffineTextAttributes>>;
|
|
23
|
+
};
|
|
21
24
|
export declare const MarkdownExtensions: ExtensionType[];
|
|
@@ -193,6 +193,24 @@ export const CodeExtension = InlineMarkdownExtension({
|
|
|
193
193
|
});
|
|
194
194
|
},
|
|
195
195
|
});
|
|
196
|
+
// typographic replacement: word -- + space => word — + space
|
|
197
|
+
// not convert: --- + space (divider shortcut), --flag + space, inside inline code
|
|
198
|
+
export const EmDashExtension = InlineMarkdownExtension({
|
|
199
|
+
name: 'em-dash',
|
|
200
|
+
pattern: /(?:^|[^-])--\s$/,
|
|
201
|
+
action: ({ inlineEditor, inlineRange, undoManager }) => {
|
|
202
|
+
const dashes = { index: inlineRange.index - 3, length: 2 };
|
|
203
|
+
const format = inlineEditor.getFormat(dashes);
|
|
204
|
+
if (format.code)
|
|
205
|
+
return;
|
|
206
|
+
undoManager.stopCapturing();
|
|
207
|
+
inlineEditor.insertText(dashes, '—', format);
|
|
208
|
+
inlineEditor.setInlineRange({
|
|
209
|
+
index: inlineRange.index - 1,
|
|
210
|
+
length: 0,
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
});
|
|
196
214
|
export const MarkdownExtensions = [
|
|
197
215
|
BoldItalicMarkdown,
|
|
198
216
|
BoldMarkdown,
|
|
@@ -201,4 +219,5 @@ export const MarkdownExtensions = [
|
|
|
201
219
|
UnderthroughExtension,
|
|
202
220
|
CodeExtension,
|
|
203
221
|
LatexExtension,
|
|
222
|
+
EmDashExtension,
|
|
204
223
|
];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
2
|
+
export interface HeadingMetrics {
|
|
3
|
+
/** Font size, px. */
|
|
4
|
+
readonly fontSize: number;
|
|
5
|
+
readonly fontWeight: number;
|
|
6
|
+
/** Letter spacing, em. */
|
|
7
|
+
readonly letterSpacing: number;
|
|
8
|
+
/** What the line box adds to the font size, px: `line-height: calc(1em + Npx)`. */
|
|
9
|
+
readonly lineHeightExtra: number;
|
|
10
|
+
/** Space above the heading, px. */
|
|
11
|
+
readonly marginTop: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const HEADING_LEVELS: readonly HeadingLevel[];
|
|
14
|
+
/**
|
|
15
|
+
* Labre's document heading scale — the single source of truth for H1…H6.
|
|
16
|
+
*
|
|
17
|
+
* The sizes step down by a ratio of about 1.25 from the doc title (40px/700)
|
|
18
|
+
* to H3, so each level reads as clearly smaller than the one above and H1
|
|
19
|
+
* never competes with the title. Below H3 the steps tighten: H4–H6 only need
|
|
20
|
+
* to stay above body text. The paragraph styles, the callout emoji and the
|
|
21
|
+
* slash-menu previews derive from this table rather than from the upstream
|
|
22
|
+
* `--affine-font-h-N` theme variables, which no longer drive document headings.
|
|
23
|
+
* The hover affordances (drag-handle grabber, heading-level icon) keep their
|
|
24
|
+
* upstream tuning on purpose: the PO prefers them aligned with each other as
|
|
25
|
+
* they were (recette 11/09/2026), not re-centred on the text line.
|
|
26
|
+
*/
|
|
27
|
+
export declare const HEADING_SCALE: Readonly<Record<HeadingLevel, HeadingMetrics>>;
|
|
28
|
+
/** Height of a heading's (single) line box, px: font size + line-height extra. */
|
|
29
|
+
export declare function headingLineBox(level: HeadingLevel): number;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export const HEADING_LEVELS = [
|
|
2
|
+
'h1',
|
|
3
|
+
'h2',
|
|
4
|
+
'h3',
|
|
5
|
+
'h4',
|
|
6
|
+
'h5',
|
|
7
|
+
'h6',
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Labre's document heading scale — the single source of truth for H1…H6.
|
|
11
|
+
*
|
|
12
|
+
* The sizes step down by a ratio of about 1.25 from the doc title (40px/700)
|
|
13
|
+
* to H3, so each level reads as clearly smaller than the one above and H1
|
|
14
|
+
* never competes with the title. Below H3 the steps tighten: H4–H6 only need
|
|
15
|
+
* to stay above body text. The paragraph styles, the callout emoji and the
|
|
16
|
+
* slash-menu previews derive from this table rather than from the upstream
|
|
17
|
+
* `--affine-font-h-N` theme variables, which no longer drive document headings.
|
|
18
|
+
* The hover affordances (drag-handle grabber, heading-level icon) keep their
|
|
19
|
+
* upstream tuning on purpose: the PO prefers them aligned with each other as
|
|
20
|
+
* they were (recette 11/09/2026), not re-centred on the text line.
|
|
21
|
+
*/
|
|
22
|
+
export const HEADING_SCALE = {
|
|
23
|
+
h1: {
|
|
24
|
+
fontSize: 32,
|
|
25
|
+
fontWeight: 700,
|
|
26
|
+
letterSpacing: -0.02,
|
|
27
|
+
lineHeightExtra: 8,
|
|
28
|
+
marginTop: 18,
|
|
29
|
+
},
|
|
30
|
+
h2: {
|
|
31
|
+
fontSize: 26,
|
|
32
|
+
fontWeight: 600,
|
|
33
|
+
letterSpacing: -0.02,
|
|
34
|
+
lineHeightExtra: 10,
|
|
35
|
+
marginTop: 14,
|
|
36
|
+
},
|
|
37
|
+
h3: {
|
|
38
|
+
fontSize: 20,
|
|
39
|
+
fontWeight: 600,
|
|
40
|
+
letterSpacing: -0.02,
|
|
41
|
+
lineHeightExtra: 8,
|
|
42
|
+
marginTop: 12,
|
|
43
|
+
},
|
|
44
|
+
h4: {
|
|
45
|
+
fontSize: 18,
|
|
46
|
+
fontWeight: 600,
|
|
47
|
+
letterSpacing: -0.015,
|
|
48
|
+
lineHeightExtra: 8,
|
|
49
|
+
marginTop: 12,
|
|
50
|
+
},
|
|
51
|
+
h5: {
|
|
52
|
+
fontSize: 16,
|
|
53
|
+
fontWeight: 600,
|
|
54
|
+
letterSpacing: -0.015,
|
|
55
|
+
lineHeightExtra: 8,
|
|
56
|
+
marginTop: 12,
|
|
57
|
+
},
|
|
58
|
+
h6: {
|
|
59
|
+
fontSize: 15,
|
|
60
|
+
fontWeight: 600,
|
|
61
|
+
letterSpacing: -0.015,
|
|
62
|
+
lineHeightExtra: 8,
|
|
63
|
+
marginTop: 12,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
/** Height of a heading's (single) line box, px: font size + line-height extra. */
|
|
67
|
+
export function headingLineBox(level) {
|
|
68
|
+
const { fontSize, lineHeightExtra } = HEADING_SCALE[level];
|
|
69
|
+
return fontSize + lineHeightExtra;
|
|
70
|
+
}
|
|
@@ -22,5 +22,7 @@ export declare const DEFAULT_IMAGE_PROXY_ENDPOINT = "https://affine-worker.toeve
|
|
|
22
22
|
export declare const DEFAULT_LINK_PREVIEW_ENDPOINT = "https://affine-worker.toeverything.workers.dev/api/worker/link-preview";
|
|
23
23
|
export declare const CANVAS_EXPORT_IGNORE_TAGS: string[];
|
|
24
24
|
export * from './bracket-pairs.js';
|
|
25
|
+
export * from './heading.js';
|
|
26
|
+
export * from './notation.js';
|
|
25
27
|
export * from './note.js';
|
|
26
28
|
export * from './text.js';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The neutral scale every framework notation on the canvas shares: inks,
|
|
3
|
+
* greys, borders and the card fill.
|
|
4
|
+
*
|
|
5
|
+
* **Wardley is the reference.** The values are the Wardley map's own (its
|
|
6
|
+
* background `COLORS`, its node stroke and fill, its legend frame), so a
|
|
7
|
+
* Wardley board is unchanged by this module existing; every other framework
|
|
8
|
+
* (BPMN, C4, EDGY, the DDD notations, the generic templates) reads its neutrals
|
|
9
|
+
* from here instead of restating a near-identical grey of its own. One scale,
|
|
10
|
+
* one place to change it.
|
|
11
|
+
*
|
|
12
|
+
* **Hues stay per framework.** An event ring, a C4 container blue, a sticky
|
|
13
|
+
* colour or an evolution red is the notation's meaning and lives in that
|
|
14
|
+
* framework's `consts.ts`. So do the neutrals a notation borrows from an
|
|
15
|
+
* official source (the C4 stencil's `#444444`, the Cynefin and Estuarine SVGs,
|
|
16
|
+
* the EDGY facet picto) and the greys that carry a meaning (a future bounded
|
|
17
|
+
* context, a Core Domain zone): those are content of the notation, not chrome.
|
|
18
|
+
*
|
|
19
|
+
* **No band tint.** The title or header strip of a board (BPMN participant
|
|
20
|
+
* band, C4 board header, a generic template's column) is plain `cardFill`
|
|
21
|
+
* white, set apart by its divider line (PO, 11/09/2026). The Wardley phase
|
|
22
|
+
* bands are not chrome: a graded wash across four zones is the map's meaning,
|
|
23
|
+
* so they stay Wardley's own.
|
|
24
|
+
*
|
|
25
|
+
* **Stored colours are never rewritten.** A creation default is copied into
|
|
26
|
+
* the element when it is placed, so changing a value here repaints only the
|
|
27
|
+
* render-time chrome of existing boards (backgrounds, cards, frames) and the
|
|
28
|
+
* elements created from now on. Any code that recognises an element by its
|
|
29
|
+
* stored colour must keep accepting the value it was created with.
|
|
30
|
+
*/
|
|
31
|
+
declare const SCALE: {
|
|
32
|
+
/**
|
|
33
|
+
* Artefact ink: node strokes, text inside or under an artefact, labels,
|
|
34
|
+
* legend text, the default connector stroke (Wardley `NODE_STROKE`).
|
|
35
|
+
*/
|
|
36
|
+
ink: string;
|
|
37
|
+
/**
|
|
38
|
+
* Background structure: axes, frame lines, board titles, pool frames and
|
|
39
|
+
* names, axis titles (Wardley `COLORS.axis`).
|
|
40
|
+
*/
|
|
41
|
+
frameInk: string;
|
|
42
|
+
/**
|
|
43
|
+
* Secondary labels drawn on a background: headings, tick labels (Wardley
|
|
44
|
+
* `COLORS.label`).
|
|
45
|
+
*/
|
|
46
|
+
label: string;
|
|
47
|
+
/**
|
|
48
|
+
* Dashed dividers, group strokes, secondary strokes (Wardley
|
|
49
|
+
* `COLORS.divider`).
|
|
50
|
+
*/
|
|
51
|
+
divider: string;
|
|
52
|
+
/** Board, card and node fill (Wardley `COLORS.card` / `NODE_FILL`). */
|
|
53
|
+
cardFill: string;
|
|
54
|
+
/** The board card's border (Wardley `COLORS.cardBorder`). */
|
|
55
|
+
cardBorder: string;
|
|
56
|
+
/** A legend's frame and separators (the Wardley legend). */
|
|
57
|
+
legendBorder: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Typed as plain strings on purpose, not `as const`: a framework constant set
|
|
61
|
+
* from the scale (`NODE_STROKE = NOTATION_NEUTRALS.ink`) must stay a `string`,
|
|
62
|
+
* as the hex literal it replaces was, so it can still be passed wherever any
|
|
63
|
+
* colour is (a helper whose parameter type is inferred from a default).
|
|
64
|
+
*/
|
|
65
|
+
export declare const NOTATION_NEUTRALS: Readonly<typeof SCALE>;
|
|
66
|
+
export type NotationNeutral = keyof typeof NOTATION_NEUTRALS;
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The neutral scale every framework notation on the canvas shares: inks,
|
|
3
|
+
* greys, borders and the card fill.
|
|
4
|
+
*
|
|
5
|
+
* **Wardley is the reference.** The values are the Wardley map's own (its
|
|
6
|
+
* background `COLORS`, its node stroke and fill, its legend frame), so a
|
|
7
|
+
* Wardley board is unchanged by this module existing; every other framework
|
|
8
|
+
* (BPMN, C4, EDGY, the DDD notations, the generic templates) reads its neutrals
|
|
9
|
+
* from here instead of restating a near-identical grey of its own. One scale,
|
|
10
|
+
* one place to change it.
|
|
11
|
+
*
|
|
12
|
+
* **Hues stay per framework.** An event ring, a C4 container blue, a sticky
|
|
13
|
+
* colour or an evolution red is the notation's meaning and lives in that
|
|
14
|
+
* framework's `consts.ts`. So do the neutrals a notation borrows from an
|
|
15
|
+
* official source (the C4 stencil's `#444444`, the Cynefin and Estuarine SVGs,
|
|
16
|
+
* the EDGY facet picto) and the greys that carry a meaning (a future bounded
|
|
17
|
+
* context, a Core Domain zone): those are content of the notation, not chrome.
|
|
18
|
+
*
|
|
19
|
+
* **No band tint.** The title or header strip of a board (BPMN participant
|
|
20
|
+
* band, C4 board header, a generic template's column) is plain `cardFill`
|
|
21
|
+
* white, set apart by its divider line (PO, 11/09/2026). The Wardley phase
|
|
22
|
+
* bands are not chrome: a graded wash across four zones is the map's meaning,
|
|
23
|
+
* so they stay Wardley's own.
|
|
24
|
+
*
|
|
25
|
+
* **Stored colours are never rewritten.** A creation default is copied into
|
|
26
|
+
* the element when it is placed, so changing a value here repaints only the
|
|
27
|
+
* render-time chrome of existing boards (backgrounds, cards, frames) and the
|
|
28
|
+
* elements created from now on. Any code that recognises an element by its
|
|
29
|
+
* stored colour must keep accepting the value it was created with.
|
|
30
|
+
*/
|
|
31
|
+
const SCALE = {
|
|
32
|
+
/**
|
|
33
|
+
* Artefact ink: node strokes, text inside or under an artefact, labels,
|
|
34
|
+
* legend text, the default connector stroke (Wardley `NODE_STROKE`).
|
|
35
|
+
*/
|
|
36
|
+
ink: '#1f2328',
|
|
37
|
+
/**
|
|
38
|
+
* Background structure: axes, frame lines, board titles, pool frames and
|
|
39
|
+
* names, axis titles (Wardley `COLORS.axis`).
|
|
40
|
+
*/
|
|
41
|
+
frameInk: '#3b3d42',
|
|
42
|
+
/**
|
|
43
|
+
* Secondary labels drawn on a background: headings, tick labels (Wardley
|
|
44
|
+
* `COLORS.label`).
|
|
45
|
+
*/
|
|
46
|
+
label: '#6b7280',
|
|
47
|
+
/**
|
|
48
|
+
* Dashed dividers, group strokes, secondary strokes (Wardley
|
|
49
|
+
* `COLORS.divider`).
|
|
50
|
+
*/
|
|
51
|
+
divider: '#9aa0a6',
|
|
52
|
+
/** Board, card and node fill (Wardley `COLORS.card` / `NODE_FILL`). */
|
|
53
|
+
cardFill: '#ffffff',
|
|
54
|
+
/** The board card's border (Wardley `COLORS.cardBorder`). */
|
|
55
|
+
cardBorder: '#e3e2e4',
|
|
56
|
+
/** A legend's frame and separators (the Wardley legend). */
|
|
57
|
+
legendBorder: '#cfd2d6',
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Typed as plain strings on purpose, not `as const`: a framework constant set
|
|
61
|
+
* from the scale (`NODE_STROKE = NOTATION_NEUTRALS.ink`) must stay a `string`,
|
|
62
|
+
* as the hex literal it replaces was, so it can still be passed wherever any
|
|
63
|
+
* colour is (a helper whose parameter type is inferred from a default).
|
|
64
|
+
*/
|
|
65
|
+
export const NOTATION_NEUTRALS = Object.freeze(SCALE);
|