@k2b/editor 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +32 -1
- package/package.json +116 -4
- package/src/BubbleMenu.tsx +126 -0
- package/src/Editor.tsx +32 -0
- package/src/SlashMenu.tsx +13 -0
- package/src/Toolbar.tsx +205 -0
- package/src/actions.ts +102 -0
- package/src/chart-schema.ts +274 -0
- package/src/charts.ts +710 -0
- package/src/collab/history.ts +39 -0
- package/src/collab/index.ts +32 -0
- package/src/collab/relative-cursor.ts +18 -0
- package/src/collab/remote-cursors.ts +358 -0
- package/src/editor-state.ts +199 -0
- package/src/formula.ts +1283 -0
- package/src/index.ts +16 -0
- package/src/markdown/_lib/cursor-zone-field.ts +338 -0
- package/src/markdown/_lib/inline-syntax.ts +9 -0
- package/src/markdown/_lib/link-opener.ts +50 -0
- package/src/markdown/_lib/list-item-line.ts +34 -0
- package/src/markdown/_lib/list-structure.ts +227 -0
- package/src/markdown/_lib/math-syntax.ts +3 -0
- package/src/markdown/_lib/table-cell.ts +74 -0
- package/src/markdown/basic.ts +37 -0
- package/src/markdown/block-previews.tsx +453 -0
- package/src/markdown/chart-block-messages.ts +5 -0
- package/src/markdown/chart-copy.ts +20 -0
- package/src/markdown/code-fence-snippets.ts +167 -0
- package/src/markdown/code-font.ts +54 -0
- package/src/markdown/completion-icon.ts +9 -0
- package/src/markdown/data-block-template.ts +11 -0
- package/src/markdown/data-blocks.ts +214 -0
- package/src/markdown/editor-scope.ts +92 -0
- package/src/markdown/fade-scroll-margins.browser-harness.ts +78 -0
- package/src/markdown/fade-scroll-margins.ts +25 -0
- package/src/markdown/image-syntax.ts +26 -0
- package/src/markdown/images.ts +192 -0
- package/src/markdown/index.ts +54 -0
- package/src/markdown/info-block-snippets.ts +159 -0
- package/src/markdown/info-blocks.ts +156 -0
- package/src/markdown/katex.ts +303 -0
- package/src/markdown/lib/chart-blocks.ts +511 -0
- package/src/markdown/lib/heading-anchors.ts +40 -0
- package/src/markdown/lib/ligatures.ts +107 -0
- package/src/markdown/lib/markdown-context.ts +72 -0
- package/src/markdown/lib/markdown-fences.ts +22 -0
- package/src/markdown/lib/markdown-image.fixtures.ts +20 -0
- package/src/markdown/lib/markdown-image.ts +36 -0
- package/src/markdown/lib/named-blocks.ts +403 -0
- package/src/markdown/lib/query-blocks.ts +145 -0
- package/src/markdown/ligatures.ts +223 -0
- package/src/markdown/links.ts +158 -0
- package/src/markdown/list-commands.ts +1124 -0
- package/src/markdown/lists.ts +290 -0
- package/src/markdown/mark.ts +103 -0
- package/src/markdown/markdown.ts +78 -0
- package/src/markdown/markup.ts +385 -0
- package/src/markdown/mermaid-export.ts +78 -0
- package/src/markdown/mermaid-preview.tsx +76 -0
- package/src/markdown/mermaid-render.ts +22 -0
- package/src/markdown/mermaid-viewport.tsx +78 -0
- package/src/markdown/mermaid.ts +242 -0
- package/src/markdown/messages.ts +17 -0
- package/src/markdown/named-blocks.ts +84 -0
- package/src/markdown/note-scroller.browser-harness.ts +84 -0
- package/src/markdown/note-scroller.ts +57 -0
- package/src/markdown/notebook-charts.tsx +142 -0
- package/src/markdown/pretty-table-messages.ts +33 -0
- package/src/markdown/pretty-table.ts +220 -0
- package/src/markdown/search-theme.ts +98 -0
- package/src/markdown/shared-tables.ts +286 -0
- package/src/markdown/sub-sup.ts +115 -0
- package/src/markdown/surface-press.ts +17 -0
- package/src/markdown/tab-indent.ts +104 -0
- package/src/markdown/table-columns.ts +188 -0
- package/src/markdown/table-formulas.ts +178 -0
- package/src/markdown/tables.ts +703 -0
- package/src/markdown/theme.ts +243 -0
- package/src/markdown/urls.ts +29 -0
- package/src/messages.ts +45 -0
- package/src/slash-menu.ts +220 -0
- package/src/styles.css +376 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/** Optional caller policy. Rendering itself has no AI input budget. */
|
|
4
|
+
export type ChartLimits = { maxSeries: number; maxValues: number; maxMarks: number };
|
|
5
|
+
|
|
6
|
+
// Every chart schema is built by a function whose call is marked pure, so a bundle that imports neither chart contract
|
|
7
|
+
// leaves them out. App frames include the app contract through `cloud.chart()`.
|
|
8
|
+
export const chartNumberFormatSchema = () =>
|
|
9
|
+
z
|
|
10
|
+
.strictObject({
|
|
11
|
+
style: z.enum(["number", "percent", "currency"]).optional(),
|
|
12
|
+
currency: z
|
|
13
|
+
.string()
|
|
14
|
+
.regex(/^[A-Z]{3}$/)
|
|
15
|
+
.optional(),
|
|
16
|
+
// Units fit after a value in the shared renderer, like the existing gauge unit.
|
|
17
|
+
unit: z.string().trim().min(1).max(12).optional(),
|
|
18
|
+
// Intl.NumberFormat supports this precision; table formats already use 0..10.
|
|
19
|
+
decimals: z.number().int().min(0).max(10).optional(),
|
|
20
|
+
compact: z.boolean().optional(),
|
|
21
|
+
})
|
|
22
|
+
.superRefine((value, ctx) => {
|
|
23
|
+
if ((value.style === "currency") !== (value.currency !== undefined))
|
|
24
|
+
ctx.addIssue({ code: "custom", path: ["currency"], message: "Currency is required exactly when style is currency." });
|
|
25
|
+
});
|
|
26
|
+
const dateFormatSchema = () => z.strictObject({ date: z.enum(["day", "minute", "second"]) });
|
|
27
|
+
export const chartFormatSchema = () => z.union([chartNumberFormatSchema(), dateFormatSchema()]);
|
|
28
|
+
export type ChartNumberFormat = z.output<ReturnType<typeof chartNumberFormatSchema>>;
|
|
29
|
+
export type ChartDateFormat = z.output<ReturnType<typeof dateFormatSchema>>;
|
|
30
|
+
export type ChartFormat = ChartNumberFormat | ChartDateFormat;
|
|
31
|
+
|
|
32
|
+
export const chartKindSchema = () =>
|
|
33
|
+
z.enum(["bar", "line", "scatter", "pie", "donut", "histogram", "gauge", "sparkline", "boxplot", "heatmap", "barGauge", "stat"]);
|
|
34
|
+
|
|
35
|
+
export const chartParts = (limits?: ChartLimits) => {
|
|
36
|
+
const ChartLabel = limits ? z.string().trim().min(1).max(80) : z.union([z.string(), z.number().transform(String)]);
|
|
37
|
+
const ChartTooltip = z
|
|
38
|
+
.strictObject({ label: ChartLabel.optional(), format: chartNumberFormatSchema().optional() })
|
|
39
|
+
.describe("All kinds: what tooltips and the data table call the value and how they format it; axes keep their own format.");
|
|
40
|
+
// Missing observations stay numeric so the renderer can omit them with its finite-value checks.
|
|
41
|
+
const ChartNumber = limits
|
|
42
|
+
? z.number()
|
|
43
|
+
: z
|
|
44
|
+
.custom<number>((value) => typeof value === "number", "Expected a number, null or undefined.")
|
|
45
|
+
.nullable()
|
|
46
|
+
.optional()
|
|
47
|
+
.transform((value) => value ?? Number.NaN);
|
|
48
|
+
const list = <T extends z.ZodType>(item: T, min: number, max?: number, message?: string) => {
|
|
49
|
+
const array = z.array(item);
|
|
50
|
+
const bounded = limits && min > 0 ? array.min(min) : array;
|
|
51
|
+
return limits && max !== undefined ? bounded.max(max, message) : bounded;
|
|
52
|
+
};
|
|
53
|
+
const ChartDate = z
|
|
54
|
+
.string()
|
|
55
|
+
.regex(/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2})?)?$/)
|
|
56
|
+
.refine((value) => chartDateTime(value) !== undefined, "Not a valid date.");
|
|
57
|
+
const ChartPoint = z.object({
|
|
58
|
+
x: z.union([ChartNumber, ChartDate]).describe("line, scatter: number or YYYY-MM-DD[THH:mm[:ss]], wall-clock time without an offset."),
|
|
59
|
+
y: ChartNumber.describe("line, scatter: value."),
|
|
60
|
+
});
|
|
61
|
+
// As many lines as series a reader can tell apart.
|
|
62
|
+
const ChartReferences = list(
|
|
63
|
+
z.object({ value: z.union([z.number(), ChartDate]), axis: z.enum(["x", "y"]).optional(), label: ChartLabel.optional() }),
|
|
64
|
+
0,
|
|
65
|
+
limits?.maxSeries,
|
|
66
|
+
`Use at most ${limits?.maxSeries} references.`,
|
|
67
|
+
);
|
|
68
|
+
// One palette color per band.
|
|
69
|
+
const ChartThresholds = list(
|
|
70
|
+
z.object({ value: z.number(), label: ChartLabel.optional() }),
|
|
71
|
+
1,
|
|
72
|
+
limits?.maxSeries,
|
|
73
|
+
`Use at most ${limits?.maxSeries} thresholds.`,
|
|
74
|
+
);
|
|
75
|
+
return {
|
|
76
|
+
ChartLabel,
|
|
77
|
+
ChartTooltip,
|
|
78
|
+
ChartNumber,
|
|
79
|
+
list,
|
|
80
|
+
ChartReferences,
|
|
81
|
+
ChartThresholds,
|
|
82
|
+
ChartAxis: z.object({
|
|
83
|
+
label: ChartLabel.optional(),
|
|
84
|
+
domain: z.array(z.number()).length(2).optional().describe("Exact [min, max]; must contain every value and reference."),
|
|
85
|
+
ticks: (limits ? z.number().int().min(2).max(12) : z.number().int().min(2)).optional(),
|
|
86
|
+
scale: z.enum(["linear", "log"]).optional().describe("log needs values above zero."),
|
|
87
|
+
format: chartFormatSchema().optional(),
|
|
88
|
+
}),
|
|
89
|
+
ChartCategory: z.object({ label: ChartLabel, value: ChartNumber }),
|
|
90
|
+
ChartSlice: z.object({ label: ChartLabel, value: limits ? z.number().min(0) : ChartNumber }),
|
|
91
|
+
ChartSeries: (points: z.ZodArray<typeof ChartPoint>) =>
|
|
92
|
+
z.object({
|
|
93
|
+
label: ChartLabel.optional(),
|
|
94
|
+
data: points,
|
|
95
|
+
role: z.literal("reference").optional(),
|
|
96
|
+
}),
|
|
97
|
+
ChartPoints: list(ChartPoint, 0, limits?.maxValues),
|
|
98
|
+
ChartBins: z
|
|
99
|
+
.union([
|
|
100
|
+
limits ? z.number().int().min(1).max(limits?.maxValues) : z.number().int().min(1),
|
|
101
|
+
limits
|
|
102
|
+
? z
|
|
103
|
+
.array(z.number())
|
|
104
|
+
.min(2)
|
|
105
|
+
.max(limits.maxValues + 1)
|
|
106
|
+
: z.array(z.number()).min(2),
|
|
107
|
+
])
|
|
108
|
+
.describe("histogram: a bin count or ascending bin edges."),
|
|
109
|
+
ChartCommon: {
|
|
110
|
+
title: (limits ? z.string().trim().min(1).max(160) : z.string()).describe("All kinds and groups: chart title."),
|
|
111
|
+
subtitle: (limits ? z.string().trim().min(1).max(300) : z.string())
|
|
112
|
+
.optional()
|
|
113
|
+
.describe("All kinds and groups: unit, period or source."),
|
|
114
|
+
},
|
|
115
|
+
ChartUnit: limits ? z.string().trim().min(1).max(12) : z.string(),
|
|
116
|
+
ChartBox: z.object({ label: ChartLabel, values: list(ChartNumber, 1) }),
|
|
117
|
+
ChartCell: z.object({ x: ChartLabel, y: ChartLabel, value: ChartNumber }),
|
|
118
|
+
ChartBarGauge: z.object({ label: ChartLabel, value: ChartNumber, min: z.number().optional(), max: z.number().optional() }),
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const chartKinds = (limits?: ChartLimits) => {
|
|
123
|
+
const {
|
|
124
|
+
ChartNumber,
|
|
125
|
+
list,
|
|
126
|
+
ChartAxis,
|
|
127
|
+
ChartBins,
|
|
128
|
+
ChartCategory,
|
|
129
|
+
ChartCommon,
|
|
130
|
+
ChartTooltip,
|
|
131
|
+
ChartLabel,
|
|
132
|
+
ChartPoints,
|
|
133
|
+
ChartSeries,
|
|
134
|
+
ChartSlice,
|
|
135
|
+
ChartReferences,
|
|
136
|
+
ChartThresholds,
|
|
137
|
+
ChartUnit,
|
|
138
|
+
ChartBox,
|
|
139
|
+
ChartCell,
|
|
140
|
+
ChartBarGauge,
|
|
141
|
+
} = chartParts(limits);
|
|
142
|
+
const ChartKindCommon = { ...ChartCommon, tooltip: ChartTooltip.optional() };
|
|
143
|
+
const numberFormat = chartNumberFormatSchema();
|
|
144
|
+
return z.discriminatedUnion("kind", [
|
|
145
|
+
z.object({
|
|
146
|
+
kind: z.literal("bar"),
|
|
147
|
+
...ChartKindCommon,
|
|
148
|
+
data: list(ChartCategory, 1, limits?.maxValues),
|
|
149
|
+
xAxis: ChartAxis.pick({ label: true }).strict().optional(),
|
|
150
|
+
yAxis: ChartAxis.optional(),
|
|
151
|
+
colorByBar: z.boolean().optional(),
|
|
152
|
+
showValues: z.boolean().optional(),
|
|
153
|
+
legend: z.boolean().optional(),
|
|
154
|
+
references: ChartReferences.optional(),
|
|
155
|
+
}),
|
|
156
|
+
z.object({
|
|
157
|
+
kind: z.literal("line"),
|
|
158
|
+
...ChartKindCommon,
|
|
159
|
+
series: list(
|
|
160
|
+
ChartSeries(
|
|
161
|
+
limits
|
|
162
|
+
? ChartPoints.min(2, "A line needs at least two points per series; show single values as a bar or scatter chart.")
|
|
163
|
+
: ChartPoints,
|
|
164
|
+
).extend({ style: z.enum(["solid", "dashed", "dotted"]).optional() }),
|
|
165
|
+
1,
|
|
166
|
+
limits?.maxSeries,
|
|
167
|
+
),
|
|
168
|
+
xAxis: ChartAxis.optional(),
|
|
169
|
+
yAxis: ChartAxis.optional(),
|
|
170
|
+
legend: z.boolean().optional(),
|
|
171
|
+
area: z.boolean().optional(),
|
|
172
|
+
smooth: z.boolean().optional(),
|
|
173
|
+
references: ChartReferences.optional(),
|
|
174
|
+
}),
|
|
175
|
+
z.object({
|
|
176
|
+
kind: z.literal("scatter"),
|
|
177
|
+
...ChartKindCommon,
|
|
178
|
+
series: list(ChartSeries(limits ? ChartPoints.min(1) : ChartPoints), 1, limits?.maxSeries),
|
|
179
|
+
xAxis: ChartAxis.optional(),
|
|
180
|
+
yAxis: ChartAxis.optional(),
|
|
181
|
+
legend: z.boolean().optional(),
|
|
182
|
+
references: ChartReferences.optional(),
|
|
183
|
+
}),
|
|
184
|
+
z.object({
|
|
185
|
+
kind: z.enum(["pie", "donut"]),
|
|
186
|
+
...ChartKindCommon,
|
|
187
|
+
data: list(ChartSlice, 1, limits?.maxValues),
|
|
188
|
+
legend: z.boolean().optional(),
|
|
189
|
+
showLabels: z.boolean().optional(),
|
|
190
|
+
format: numberFormat.optional(),
|
|
191
|
+
}),
|
|
192
|
+
z.object({
|
|
193
|
+
kind: z.literal("histogram"),
|
|
194
|
+
...ChartKindCommon,
|
|
195
|
+
data: list(ChartNumber, 1, limits?.maxValues),
|
|
196
|
+
bins: ChartBins.optional(),
|
|
197
|
+
references: ChartReferences.optional(),
|
|
198
|
+
}),
|
|
199
|
+
z.object({
|
|
200
|
+
kind: z.literal("gauge"),
|
|
201
|
+
...ChartKindCommon,
|
|
202
|
+
value: ChartNumber,
|
|
203
|
+
min: z.number().optional(),
|
|
204
|
+
max: z.number().optional(),
|
|
205
|
+
label: ChartLabel.optional(),
|
|
206
|
+
unit: ChartUnit.optional(),
|
|
207
|
+
format: numberFormat.optional(),
|
|
208
|
+
thresholds: ChartThresholds.optional(),
|
|
209
|
+
}),
|
|
210
|
+
z.object({
|
|
211
|
+
kind: z.literal("sparkline"),
|
|
212
|
+
...ChartKindCommon,
|
|
213
|
+
data: list(ChartNumber, 2, limits?.maxValues),
|
|
214
|
+
area: z.boolean().optional(),
|
|
215
|
+
}),
|
|
216
|
+
z.object({
|
|
217
|
+
kind: z.literal("boxplot"),
|
|
218
|
+
...ChartKindCommon,
|
|
219
|
+
data: list(ChartBox, 1, limits?.maxValues),
|
|
220
|
+
yAxis: ChartAxis.optional(),
|
|
221
|
+
references: ChartReferences.optional(),
|
|
222
|
+
}),
|
|
223
|
+
z.object({
|
|
224
|
+
kind: z.literal("heatmap"),
|
|
225
|
+
...ChartKindCommon,
|
|
226
|
+
data: list(ChartCell, 1, limits?.maxMarks),
|
|
227
|
+
format: numberFormat.optional(),
|
|
228
|
+
showValues: z.boolean().optional(),
|
|
229
|
+
min: z.number().optional(),
|
|
230
|
+
max: z.number().optional(),
|
|
231
|
+
}),
|
|
232
|
+
z.object({
|
|
233
|
+
kind: z.literal("barGauge"),
|
|
234
|
+
...ChartKindCommon,
|
|
235
|
+
// The shared renderer draws 280 units high and a bar gauge row is 28 units, so 8 rows fit without a taller drawing.
|
|
236
|
+
data: list(ChartBarGauge, 1, limits?.maxSeries, `Use at most ${limits?.maxSeries} bar gauge items.`),
|
|
237
|
+
min: z.number().optional(),
|
|
238
|
+
max: z.number().optional(),
|
|
239
|
+
unit: ChartUnit.optional(),
|
|
240
|
+
format: numberFormat.optional(),
|
|
241
|
+
thresholds: ChartThresholds.optional(),
|
|
242
|
+
}),
|
|
243
|
+
z.object({
|
|
244
|
+
kind: z.literal("stat"),
|
|
245
|
+
...ChartKindCommon,
|
|
246
|
+
value: ChartNumber,
|
|
247
|
+
format: numberFormat.optional(),
|
|
248
|
+
unit: ChartUnit.optional(),
|
|
249
|
+
delta: limits ? z.number().optional() : ChartNumber,
|
|
250
|
+
better: z.enum(["up", "down"]).optional(),
|
|
251
|
+
sparkline: list(ChartNumber, 2, limits?.maxValues).optional(),
|
|
252
|
+
}),
|
|
253
|
+
]);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const DATE = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2}))?)?$/;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* A chart date as UTC milliseconds, so calendar days and wall-clock times stay as written on every device. Impossible
|
|
260
|
+
* dates such as February 30 are undefined: engines would move them into March or refuse them, each in its own way.
|
|
261
|
+
*/
|
|
262
|
+
export const chartDateTime = (value: string): number | undefined => {
|
|
263
|
+
const match = DATE.exec(value);
|
|
264
|
+
if (!match) return undefined;
|
|
265
|
+
const [, year, month, day, hour = "00", minute = "00", second = "00"] = match;
|
|
266
|
+
const date = new Date(0);
|
|
267
|
+
date.setUTCFullYear(Number(year), Number(month) - 1, Number(day));
|
|
268
|
+
date.setUTCHours(Number(hour), Number(minute), Number(second));
|
|
269
|
+
// Parts out of range roll over, so a date that does not read back as written does not exist.
|
|
270
|
+
return date.toISOString().startsWith(`${year}-${month}-${day}T${hour}:${minute}:${second}`) ? date.getTime() : undefined;
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
/** One chart as the shared renderers read it. */
|
|
274
|
+
export type ChartInput = z.output<ReturnType<typeof chartKinds>>;
|