@inshapardaz/likhari-core 0.1.0-dev.10

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,174 @@
1
+ /**
2
+ * EditorFeatureConfig — the single nested config object every plugin reads from.
3
+ * Mirrors lexical-editor-spec.md §5 verbatim: a feature's toggle controls
4
+ * toolbar/insert *availability*, not node registration (see
5
+ * editor-architecture-design.md §2) — all custom node types stay registered
6
+ * regardless of this config.
7
+ */
8
+ export interface EditorFeatureConfig {
9
+ formatting?: {
10
+ bold?: boolean;
11
+ italic?: boolean;
12
+ underline?: boolean;
13
+ strikethrough?: boolean;
14
+ superscript?: boolean;
15
+ subscript?: boolean;
16
+ /** uppercase/lowercase/capitalize — one-shot text transforms, not format flags */
17
+ caseTransforms?: boolean;
18
+ clearFormatting?: boolean;
19
+ };
20
+ lists?: {
21
+ numbered?: boolean;
22
+ bullet?: boolean;
23
+ check?: boolean;
24
+ };
25
+ blocks?: {
26
+ headingLevels?: (1 | 2 | 3 | 4 | 5 | 6)[];
27
+ quote?: boolean;
28
+ horizontalRule?: boolean;
29
+ pageBreak?: boolean;
30
+ };
31
+ alignment?: {
32
+ left?: boolean;
33
+ right?: boolean;
34
+ center?: boolean;
35
+ justify?: boolean;
36
+ start?: boolean;
37
+ };
38
+ indent?: boolean;
39
+ font?: {
40
+ family?: boolean;
41
+ size?: boolean;
42
+ scope?: 'selection' | 'document' | 'both';
43
+ };
44
+ color?: {
45
+ foreground?: boolean;
46
+ background?: boolean;
47
+ palette?: string[];
48
+ };
49
+ /** undo/redo */
50
+ history?: boolean;
51
+ links?: boolean;
52
+ images?: {
53
+ linked?: boolean;
54
+ embedded?: boolean;
55
+ caption?: boolean;
56
+ maxSizeMB?: number;
57
+ };
58
+ tables?: boolean;
59
+ columns?: boolean;
60
+ footnotes?: boolean;
61
+ poetry?: {
62
+ enabled?: boolean;
63
+ defaultLayout?: 'single' | 'two-column';
64
+ };
65
+ findReplace?: boolean;
66
+ language?: {
67
+ spellCheck?: boolean;
68
+ thesaurus?: boolean;
69
+ autocomplete?: boolean;
70
+ autocorrect?: boolean;
71
+ textCleanup?: boolean;
72
+ };
73
+ formats?: {
74
+ markdown?: boolean;
75
+ html?: boolean;
76
+ plainText?: boolean;
77
+ lexicalJson?: boolean;
78
+ };
79
+ }
80
+ /** A config with every optional field resolved — what plugins actually read. */
81
+ export type ResolvedEditorFeatureConfig = Required<{
82
+ [K in keyof EditorFeatureConfig]: EditorFeatureConfig[K] extends object | undefined ? Required<NonNullable<EditorFeatureConfig[K]>> : NonNullable<EditorFeatureConfig[K]>;
83
+ }>;
84
+ export declare const FULL_FEATURE_CONFIG: ResolvedEditorFeatureConfig;
85
+ /** Bare minimum: paragraphs, plain text in/out, undo/redo. No formatting chrome at all. */
86
+ export declare const MINIMAL_FEATURE_CONFIG: EditorFeatureConfig;
87
+ /** Common document editing, no verse/columns/footnotes/table extras. */
88
+ export declare const STANDARD_FEATURE_CONFIG: EditorFeatureConfig;
89
+ /** Standard, plus poetry mode on and Urdu/Punjabi-oriented defaults. */
90
+ export declare const POETRY_FEATURE_CONFIG: EditorFeatureConfig;
91
+ export declare const FEATURE_CONFIG_PRESETS: {
92
+ readonly minimal: EditorFeatureConfig;
93
+ readonly standard: EditorFeatureConfig;
94
+ readonly full: Required<{
95
+ formatting?: Required<{
96
+ bold?: boolean;
97
+ italic?: boolean;
98
+ underline?: boolean;
99
+ strikethrough?: boolean;
100
+ superscript?: boolean;
101
+ subscript?: boolean;
102
+ /** uppercase/lowercase/capitalize — one-shot text transforms, not format flags */
103
+ caseTransforms?: boolean;
104
+ clearFormatting?: boolean;
105
+ }> | undefined;
106
+ lists?: Required<{
107
+ numbered?: boolean;
108
+ bullet?: boolean;
109
+ check?: boolean;
110
+ }> | undefined;
111
+ blocks?: Required<{
112
+ headingLevels?: (1 | 2 | 3 | 4 | 5 | 6)[];
113
+ quote?: boolean;
114
+ horizontalRule?: boolean;
115
+ pageBreak?: boolean;
116
+ }> | undefined;
117
+ alignment?: Required<{
118
+ left?: boolean;
119
+ right?: boolean;
120
+ center?: boolean;
121
+ justify?: boolean;
122
+ start?: boolean;
123
+ }> | undefined;
124
+ indent?: NonNullable<boolean | undefined> | undefined;
125
+ font?: Required<{
126
+ family?: boolean;
127
+ size?: boolean;
128
+ scope?: "selection" | "document" | "both";
129
+ }> | undefined;
130
+ color?: Required<{
131
+ foreground?: boolean;
132
+ background?: boolean;
133
+ palette?: string[];
134
+ }> | undefined;
135
+ history?: NonNullable<boolean | undefined> | undefined;
136
+ links?: NonNullable<boolean | undefined> | undefined;
137
+ images?: Required<{
138
+ linked?: boolean;
139
+ embedded?: boolean;
140
+ caption?: boolean;
141
+ maxSizeMB?: number;
142
+ }> | undefined;
143
+ tables?: NonNullable<boolean | undefined> | undefined;
144
+ columns?: NonNullable<boolean | undefined> | undefined;
145
+ footnotes?: NonNullable<boolean | undefined> | undefined;
146
+ poetry?: Required<{
147
+ enabled?: boolean;
148
+ defaultLayout?: "single" | "two-column";
149
+ }> | undefined;
150
+ findReplace?: NonNullable<boolean | undefined> | undefined;
151
+ language?: Required<{
152
+ spellCheck?: boolean;
153
+ thesaurus?: boolean;
154
+ autocomplete?: boolean;
155
+ autocorrect?: boolean;
156
+ textCleanup?: boolean;
157
+ }> | undefined;
158
+ formats?: Required<{
159
+ markdown?: boolean;
160
+ html?: boolean;
161
+ plainText?: boolean;
162
+ lexicalJson?: boolean;
163
+ }> | undefined;
164
+ }>;
165
+ readonly poetry: EditorFeatureConfig;
166
+ };
167
+ export type FeatureConfigPresetName = keyof typeof FEATURE_CONFIG_PRESETS;
168
+ /**
169
+ * Resolves a (possibly partial) EditorFeatureConfig into one with every field
170
+ * present, starting from "everything off", layering the named preset
171
+ * (default: 'full') on top, then the caller's own overrides.
172
+ */
173
+ export declare function resolveFeatureConfig(config?: EditorFeatureConfig, preset?: FeatureConfigPresetName): ResolvedEditorFeatureConfig;
174
+ //# sourceMappingURL=featureConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featureConfig.d.ts","sourceRoot":"","sources":["../../src/config/featureConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,175 @@
1
+ export const FULL_FEATURE_CONFIG = {
2
+ formatting: {
3
+ bold: true,
4
+ italic: true,
5
+ underline: true,
6
+ strikethrough: true,
7
+ superscript: true,
8
+ subscript: true,
9
+ caseTransforms: true,
10
+ clearFormatting: true,
11
+ },
12
+ lists: { numbered: true, bullet: true, check: true },
13
+ blocks: {
14
+ headingLevels: [1, 2, 3, 4, 5, 6],
15
+ quote: true,
16
+ horizontalRule: true,
17
+ pageBreak: true,
18
+ },
19
+ alignment: { left: true, right: true, center: true, justify: true, start: true },
20
+ indent: true,
21
+ font: { family: true, size: true, scope: 'both' },
22
+ color: {
23
+ foreground: true,
24
+ background: true,
25
+ palette: ['#2B6E6E', '#B3462C', '#6B4E71', '#1E1E1C', '#5C5A54'],
26
+ },
27
+ history: true,
28
+ links: true,
29
+ images: { linked: true, embedded: true, caption: true, maxSizeMB: 5 },
30
+ tables: true,
31
+ columns: true,
32
+ footnotes: true,
33
+ poetry: { enabled: true, defaultLayout: 'single' },
34
+ findReplace: true,
35
+ language: {
36
+ spellCheck: true,
37
+ thesaurus: true,
38
+ autocomplete: true,
39
+ autocorrect: true,
40
+ textCleanup: true,
41
+ },
42
+ formats: { markdown: true, html: true, plainText: true, lexicalJson: true },
43
+ };
44
+ /** Bare minimum: paragraphs, plain text in/out, undo/redo. No formatting chrome at all. */
45
+ export const MINIMAL_FEATURE_CONFIG = {
46
+ formatting: {},
47
+ lists: {},
48
+ blocks: { headingLevels: [] },
49
+ alignment: {},
50
+ indent: false,
51
+ font: {},
52
+ color: {},
53
+ history: true,
54
+ links: false,
55
+ images: {},
56
+ tables: false,
57
+ columns: false,
58
+ footnotes: false,
59
+ poetry: { enabled: false },
60
+ findReplace: false,
61
+ language: {},
62
+ formats: { plainText: true, lexicalJson: true },
63
+ };
64
+ /** Common document editing, no verse/columns/footnotes/table extras. */
65
+ export const STANDARD_FEATURE_CONFIG = {
66
+ formatting: {
67
+ bold: true,
68
+ italic: true,
69
+ underline: true,
70
+ strikethrough: true,
71
+ clearFormatting: true,
72
+ },
73
+ lists: { numbered: true, bullet: true, check: true },
74
+ blocks: { headingLevels: [1, 2, 3], quote: true, horizontalRule: true },
75
+ // Left/right stay off by default — the mockup's primary toolbar only shows
76
+ // logical start/center/end/justify; literal side overrides are a poetry-mode
77
+ // affordance (spec §3.2, §4.11), not a general-editing control.
78
+ alignment: { center: true, justify: true, start: true },
79
+ indent: true,
80
+ font: {},
81
+ color: {},
82
+ history: true,
83
+ links: true,
84
+ images: { linked: true, embedded: true, caption: true },
85
+ tables: false,
86
+ columns: false,
87
+ footnotes: false,
88
+ poetry: { enabled: false },
89
+ findReplace: true,
90
+ language: {},
91
+ formats: { markdown: true, plainText: true, lexicalJson: true },
92
+ };
93
+ /** Standard, plus poetry mode on and Urdu/Punjabi-oriented defaults. */
94
+ export const POETRY_FEATURE_CONFIG = {
95
+ ...STANDARD_FEATURE_CONFIG,
96
+ // Literal left/right overrides matter here: per-couplet alignment override
97
+ // is one of the four poetry templates (spec §4.11), independent of the
98
+ // block's logical direction.
99
+ alignment: { ...STANDARD_FEATURE_CONFIG.alignment, left: true, right: true },
100
+ poetry: { enabled: true, defaultLayout: 'single' },
101
+ };
102
+ export const FEATURE_CONFIG_PRESETS = {
103
+ minimal: MINIMAL_FEATURE_CONFIG,
104
+ standard: STANDARD_FEATURE_CONFIG,
105
+ full: FULL_FEATURE_CONFIG,
106
+ poetry: POETRY_FEATURE_CONFIG,
107
+ };
108
+ function isPlainObject(value) {
109
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
110
+ }
111
+ /** Deep-merges override onto base; arrays and primitives replace, objects merge key-by-key. */
112
+ function deepMerge(base, override) {
113
+ if (!override)
114
+ return base;
115
+ const result = { ...base };
116
+ for (const key of Object.keys(override)) {
117
+ const overrideValue = override[key];
118
+ const baseValue = result[key];
119
+ result[key] =
120
+ isPlainObject(baseValue) && isPlainObject(overrideValue)
121
+ ? deepMerge(baseValue, overrideValue)
122
+ : overrideValue;
123
+ }
124
+ return result;
125
+ }
126
+ /**
127
+ * Everything off — the merge base for resolveFeatureConfig. A preset that
128
+ * omits a sub-field (e.g. minimal's `formatting: {}`) means "off", not
129
+ * "inherit the default": merging onto FULL_FEATURE_CONFIG directly would
130
+ * leave those omitted booleans at their (true) default instead, since
131
+ * deepMerge only touches keys the override actually specifies.
132
+ */
133
+ const ALL_OFF_FEATURE_CONFIG = {
134
+ formatting: {
135
+ bold: false,
136
+ italic: false,
137
+ underline: false,
138
+ strikethrough: false,
139
+ superscript: false,
140
+ subscript: false,
141
+ caseTransforms: false,
142
+ clearFormatting: false,
143
+ },
144
+ lists: { numbered: false, bullet: false, check: false },
145
+ blocks: { headingLevels: [], quote: false, horizontalRule: false, pageBreak: false },
146
+ alignment: { left: false, right: false, center: false, justify: false, start: false },
147
+ indent: false,
148
+ font: { family: false, size: false, scope: 'selection' },
149
+ color: { foreground: false, background: false, palette: [] },
150
+ history: false,
151
+ links: false,
152
+ images: { linked: false, embedded: false, caption: false, maxSizeMB: 0 },
153
+ tables: false,
154
+ columns: false,
155
+ footnotes: false,
156
+ poetry: { enabled: false, defaultLayout: 'single' },
157
+ findReplace: false,
158
+ language: {
159
+ spellCheck: false,
160
+ thesaurus: false,
161
+ autocomplete: false,
162
+ autocorrect: false,
163
+ textCleanup: false,
164
+ },
165
+ formats: { markdown: false, html: false, plainText: false, lexicalJson: false },
166
+ };
167
+ /**
168
+ * Resolves a (possibly partial) EditorFeatureConfig into one with every field
169
+ * present, starting from "everything off", layering the named preset
170
+ * (default: 'full') on top, then the caller's own overrides.
171
+ */
172
+ export function resolveFeatureConfig(config, preset = 'full') {
173
+ const base = deepMerge(ALL_OFF_FEATURE_CONFIG, FEATURE_CONFIG_PRESETS[preset]);
174
+ return deepMerge(base, config);
175
+ }
@@ -0,0 +1,4 @@
1
+ export * from './config/featureConfig.js';
2
+ export * from './theme/tokens.js';
3
+ export * from './theme/theme.css.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './config/featureConfig.js';
2
+ export * from './theme/tokens.js';
3
+ export * from './theme/theme.css.js';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * A ready-to-inject CSS string defining `--editor-*` custom properties for
3
+ * light and dark mode. Used by @inshapardaz/likhari-react's default theme provider and
4
+ * available standalone for Web Component / non-React consumers (spec §10)
5
+ * who can't pass a Mantine theme object directly.
6
+ *
7
+ * `selector` should match whatever element actually carries
8
+ * `data-editor-color-scheme` — pass the host's own selector (e.g. a wrapper
9
+ * class) rather than the default `:root` unless that attribute is genuinely
10
+ * set on the document root, or the dark/light override below will never
11
+ * match and only the `prefers-color-scheme` media query will ever apply.
12
+ */
13
+ export declare function buildThemeCss(selector?: string): string;
14
+ //# sourceMappingURL=theme.css.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.css.d.ts","sourceRoot":"","sources":["../../src/theme/theme.css.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,QAAQ,SAAU,GAAG,MAAM,CAgBxD"}
@@ -0,0 +1,30 @@
1
+ import { DARK_TOKENS, LIGHT_TOKENS, tokensToCssVars } from './tokens.js';
2
+ /**
3
+ * A ready-to-inject CSS string defining `--editor-*` custom properties for
4
+ * light and dark mode. Used by @inshapardaz/likhari-react's default theme provider and
5
+ * available standalone for Web Component / non-React consumers (spec §10)
6
+ * who can't pass a Mantine theme object directly.
7
+ *
8
+ * `selector` should match whatever element actually carries
9
+ * `data-editor-color-scheme` — pass the host's own selector (e.g. a wrapper
10
+ * class) rather than the default `:root` unless that attribute is genuinely
11
+ * set on the document root, or the dark/light override below will never
12
+ * match and only the `prefers-color-scheme` media query will ever apply.
13
+ */
14
+ export function buildThemeCss(selector = ':root') {
15
+ return `
16
+ ${selector} {
17
+ ${tokensToCssVars(LIGHT_TOKENS)}
18
+ }
19
+
20
+ ${selector}[data-editor-color-scheme='dark'] {
21
+ ${tokensToCssVars(DARK_TOKENS)}
22
+ }
23
+
24
+ @media (prefers-color-scheme: dark) {
25
+ ${selector}:not([data-editor-color-scheme='light']) {
26
+ ${tokensToCssVars(DARK_TOKENS)}
27
+ }
28
+ }
29
+ `.trim();
30
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Design tokens from editor-ui-design-spec.md §2. Consumed both as plain JS
3
+ * values (Mantine theme mapping in @inshapardaz/likhari-react) and re-exported as CSS
4
+ * custom properties (theme.css) for non-React / Web Component consumers.
5
+ */
6
+ export interface ColorTokens {
7
+ paper: string;
8
+ paperDim: string;
9
+ ink: string;
10
+ inkSoft: string;
11
+ line: string;
12
+ lineStrong: string;
13
+ accent: string;
14
+ accentSoft: string;
15
+ poetry: string;
16
+ danger: string;
17
+ }
18
+ export declare const LIGHT_TOKENS: ColorTokens;
19
+ export declare const DARK_TOKENS: ColorTokens;
20
+ /** Canvas type scale, editor-ui-design-spec.md §4 — do not share one value across scripts. */
21
+ export declare const TYPE_SCALE: {
22
+ readonly latin: {
23
+ readonly fontFamily: "'IBM Plex Serif', Georgia, serif";
24
+ readonly body: {
25
+ readonly fontSize: "15px";
26
+ readonly lineHeight: 1.75;
27
+ };
28
+ readonly h1: {
29
+ readonly fontSize: "1.8em";
30
+ readonly fontWeight: 600;
31
+ };
32
+ readonly h2: {
33
+ readonly fontSize: "1.5em";
34
+ readonly fontWeight: 600;
35
+ };
36
+ readonly h3: {
37
+ readonly fontSize: "1.3em";
38
+ readonly fontWeight: 600;
39
+ };
40
+ readonly h4: {
41
+ readonly fontSize: "1.2em";
42
+ readonly fontWeight: 600;
43
+ };
44
+ readonly h5: {
45
+ readonly fontSize: "1.1em";
46
+ readonly fontWeight: 600;
47
+ };
48
+ readonly h6: {
49
+ readonly fontSize: "1.05em";
50
+ readonly fontWeight: 600;
51
+ };
52
+ readonly poetry: {
53
+ readonly fontSize: "15px";
54
+ readonly lineHeight: 1.6;
55
+ };
56
+ };
57
+ readonly nastaliq: {
58
+ readonly fontFamily: "'Noto Nastaliq Urdu', serif";
59
+ readonly body: {
60
+ readonly fontSize: "20px";
61
+ readonly lineHeight: 2.3;
62
+ };
63
+ readonly h1: {
64
+ readonly fontSize: "1.8em";
65
+ readonly fontWeight: 600;
66
+ };
67
+ readonly h2: {
68
+ readonly fontSize: "1.5em";
69
+ readonly fontWeight: 600;
70
+ };
71
+ readonly h3: {
72
+ readonly fontSize: "1.3em";
73
+ readonly fontWeight: 600;
74
+ };
75
+ readonly h4: {
76
+ readonly fontSize: "1.2em";
77
+ readonly fontWeight: 600;
78
+ };
79
+ readonly h5: {
80
+ readonly fontSize: "1.1em";
81
+ readonly fontWeight: 600;
82
+ };
83
+ readonly h6: {
84
+ readonly fontSize: "1.05em";
85
+ readonly fontWeight: 600;
86
+ };
87
+ readonly poetry: {
88
+ readonly fontSize: "20px";
89
+ readonly lineHeight: 2.1;
90
+ };
91
+ };
92
+ };
93
+ export declare const CHROME_FONT_FAMILY = "'IBM Plex Sans', system-ui, sans-serif";
94
+ export declare const TOOLBAR: {
95
+ height: string;
96
+ buttonSize: string;
97
+ buttonGap: string;
98
+ iconSize: string;
99
+ labelSize: string;
100
+ dividerWidth: string;
101
+ };
102
+ export declare const POETRY_SPACING: {
103
+ coupletGap: string;
104
+ paragraphGap: string;
105
+ };
106
+ /** Renders a ColorTokens object as `--name: value;` lines for a CSS custom-property block. */
107
+ export declare function tokensToCssVars(tokens: ColorTokens, prefix?: string): string;
108
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/theme/tokens.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,YAAY,EAAE,WAW1B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,WAWzB,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuBb,CAAC;AAEX,eAAO,MAAM,kBAAkB,2CAA2C,CAAC;AAE3E,eAAO,MAAM,OAAO;;;;;;;CAOnB,CAAC;AAEF,eAAO,MAAM,cAAc;;;CAG1B,CAAC;AAEF,8FAA8F;AAC9F,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,SAAc,GAAG,MAAM,CAOjF"}
@@ -0,0 +1,71 @@
1
+ export const LIGHT_TOKENS = {
2
+ paper: '#F6F5F2',
3
+ paperDim: '#EDEBE6',
4
+ ink: '#1E1E1C',
5
+ inkSoft: '#5C5A54',
6
+ line: '#DAD7CE',
7
+ lineStrong: '#BEBAAE',
8
+ accent: '#2B6E6E',
9
+ accentSoft: '#E4EFEE',
10
+ poetry: '#6B4E71',
11
+ danger: '#B3462C',
12
+ };
13
+ export const DARK_TOKENS = {
14
+ paper: '#1B1B19',
15
+ paperDim: '#242422',
16
+ ink: '#EDEBE4',
17
+ inkSoft: '#A6A398',
18
+ line: '#3A3934',
19
+ lineStrong: '#4E4C45',
20
+ accent: '#6FBFBF',
21
+ accentSoft: '#233333',
22
+ poetry: '#C79ECB',
23
+ danger: '#E08A73',
24
+ };
25
+ /** Canvas type scale, editor-ui-design-spec.md §4 — do not share one value across scripts. */
26
+ export const TYPE_SCALE = {
27
+ latin: {
28
+ fontFamily: `'IBM Plex Serif', Georgia, serif`,
29
+ body: { fontSize: '15px', lineHeight: 1.75 },
30
+ h1: { fontSize: '1.8em', fontWeight: 600 },
31
+ h2: { fontSize: '1.5em', fontWeight: 600 },
32
+ h3: { fontSize: '1.3em', fontWeight: 600 },
33
+ h4: { fontSize: '1.2em', fontWeight: 600 },
34
+ h5: { fontSize: '1.1em', fontWeight: 600 },
35
+ h6: { fontSize: '1.05em', fontWeight: 600 },
36
+ poetry: { fontSize: '15px', lineHeight: 1.6 },
37
+ },
38
+ nastaliq: {
39
+ fontFamily: `'Noto Nastaliq Urdu', serif`,
40
+ body: { fontSize: '20px', lineHeight: 2.3 },
41
+ h1: { fontSize: '1.8em', fontWeight: 600 },
42
+ h2: { fontSize: '1.5em', fontWeight: 600 },
43
+ h3: { fontSize: '1.3em', fontWeight: 600 },
44
+ h4: { fontSize: '1.2em', fontWeight: 600 },
45
+ h5: { fontSize: '1.1em', fontWeight: 600 },
46
+ h6: { fontSize: '1.05em', fontWeight: 600 },
47
+ poetry: { fontSize: '20px', lineHeight: 2.1 },
48
+ },
49
+ };
50
+ export const CHROME_FONT_FAMILY = `'IBM Plex Sans', system-ui, sans-serif`;
51
+ export const TOOLBAR = {
52
+ height: '46px',
53
+ buttonSize: '30px',
54
+ buttonGap: '8px',
55
+ iconSize: '15px',
56
+ labelSize: '12px',
57
+ dividerWidth: '0.5px',
58
+ };
59
+ export const POETRY_SPACING = {
60
+ coupletGap: '1.4em',
61
+ paragraphGap: '1em',
62
+ };
63
+ /** Renders a ColorTokens object as `--name: value;` lines for a CSS custom-property block. */
64
+ export function tokensToCssVars(tokens, prefix = '--editor-') {
65
+ return Object.entries(tokens)
66
+ .map(([key, value]) => {
67
+ const kebab = key.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
68
+ return `${prefix}${kebab}: ${value};`;
69
+ })
70
+ .join('\n ');
71
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@inshapardaz/likhari-core",
3
+ "version": "0.1.0-dev.10",
4
+ "description": "Framework-agnostic pieces of the Likhari editor: feature-config schema, presets, and default theme tokens.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/inshapardaz/likhari"
9
+ },
10
+ "type": "module",
11
+ "main": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ },
18
+ "./package.json": "./package.json"
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "sideEffects": false,
27
+ "scripts": {
28
+ "build": "tsc -p tsconfig.build.json && node ../../scripts/fix-esm-extensions.mjs ./dist",
29
+ "prepublishOnly": "npm run build"
30
+ }
31
+ }