@aicayzer/inkkit 0.0.0-stage → 0.0.1

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/style.css ADDED
@@ -0,0 +1,537 @@
1
+ @import '@milkdown/kit/prose/tables/style/tables.css';
2
+
3
+ :root {
4
+ /* The document's text size, which the app sets; every text metric below is in rem, so they scale with it. */
5
+ font-size: 15px;
6
+ --accent: #0a84ff;
7
+ --text: CanvasText;
8
+ --muted: color-mix(in srgb, CanvasText 55%, transparent);
9
+ --rule: color-mix(in srgb, CanvasText 14%, transparent);
10
+ --code-bg: color-mix(in srgb, CanvasText 8%, transparent);
11
+ --mono: ui-monospace, 'SF Mono', Menlo, monospace;
12
+ color-scheme: light dark;
13
+ }
14
+
15
+ .inkkit-plain {
16
+ box-sizing: border-box;
17
+ width: 100%;
18
+ min-height: 100%;
19
+ border: 0;
20
+ resize: none;
21
+ outline: none;
22
+ padding: 10px 16px 48px;
23
+ background: transparent;
24
+ color: var(--text);
25
+ font: 1rem/1.5 var(--mono);
26
+ }
27
+
28
+ .inkkit-plain[hidden] {
29
+ display: none;
30
+ }
31
+
32
+ .milkdown-table-block {
33
+ position: relative;
34
+ margin: 1.5rem 0;
35
+ }
36
+ .milkdown-table-block .table-wrapper {
37
+ overflow-x: auto;
38
+ }
39
+ .milkdown-table-block table {
40
+ border-collapse: collapse;
41
+ }
42
+ .milkdown-table-block th,
43
+ .milkdown-table-block td {
44
+ border: 1px solid var(--rule);
45
+ min-width: 3rem;
46
+ padding: 0.4rem 0.6rem;
47
+ vertical-align: top;
48
+ }
49
+ .milkdown-table-block th {
50
+ background: var(--code-bg);
51
+ }
52
+ .milkdown-table-block th p,
53
+ .milkdown-table-block td p {
54
+ margin: 0;
55
+ }
56
+ .milkdown-table-block .handle,
57
+ .milkdown-table-block .button-group,
58
+ .milkdown-table-block .drag-preview {
59
+ position: absolute;
60
+ z-index: 20;
61
+ }
62
+ .milkdown-table-block [data-show='false'],
63
+ .milkdown-table-block .button-group[data-show='false'],
64
+ .milkdown-table-block.readonly .handle {
65
+ display: none;
66
+ }
67
+ .milkdown-table-block .cell-handle {
68
+ cursor: grab;
69
+ font-size: 0.75rem;
70
+ padding: 2px 5px;
71
+ border-radius: 5px;
72
+ background: Canvas;
73
+ color: var(--muted);
74
+ box-shadow: 0 0 0 1px var(--rule);
75
+ }
76
+ .milkdown-table-block .button-group {
77
+ display: flex;
78
+ gap: 3px;
79
+ padding: 4px;
80
+ border-radius: 6px;
81
+ background: Canvas;
82
+ box-shadow: 0 0 0 1px var(--rule);
83
+ }
84
+ .milkdown-table-block button {
85
+ color: var(--text);
86
+ background: Canvas;
87
+ border: 1px solid var(--rule);
88
+ border-radius: 4px;
89
+ padding: 4px 6px;
90
+ cursor: pointer;
91
+ white-space: nowrap;
92
+ }
93
+ .milkdown-table-block button:hover {
94
+ background: var(--code-bg);
95
+ }
96
+ .milkdown-table-block .line-handle {
97
+ background: var(--accent);
98
+ min-width: 1px;
99
+ min-height: 1px;
100
+ }
101
+ .milkdown-table-block .add-button {
102
+ position: absolute;
103
+ padding: 2px 5px;
104
+ transform: translate(-50%, -50%);
105
+ background: Canvas;
106
+ color: var(--text);
107
+ border: 1px solid var(--rule);
108
+ border-radius: 4px;
109
+ white-space: nowrap;
110
+ cursor: pointer;
111
+ }
112
+ .milkdown-table-block .line-handle[data-display-type='indicator'] .add-button {
113
+ display: none;
114
+ }
115
+ .milkdown-table-block .drag-preview {
116
+ opacity: 0.6;
117
+ background: Canvas;
118
+ outline: 1px solid var(--accent);
119
+ }
120
+
121
+ .ProseMirror .image {
122
+ position: relative;
123
+ display: inline-block;
124
+ max-width: 100%;
125
+ vertical-align: bottom;
126
+ }
127
+ .ProseMirror .image img {
128
+ display: block;
129
+ max-width: 100%;
130
+ height: auto;
131
+ border-radius: 6px;
132
+ }
133
+ .ProseMirror .image-absent {
134
+ color: var(--muted);
135
+ font-style: italic;
136
+ }
137
+ .ProseMirror .image-handle {
138
+ position: absolute;
139
+ right: -3px;
140
+ top: 0;
141
+ width: 10px;
142
+ height: 100%;
143
+ cursor: col-resize;
144
+ opacity: 0;
145
+ }
146
+ .ProseMirror .image:hover .image-handle,
147
+ .ProseMirror .image-handle:active {
148
+ opacity: 1;
149
+ }
150
+ .ProseMirror .image-handle::after {
151
+ content: '';
152
+ position: absolute;
153
+ right: 3px;
154
+ top: 25%;
155
+ height: 50%;
156
+ width: 4px;
157
+ border-radius: 2px;
158
+ background: var(--accent);
159
+ }
160
+
161
+ html,
162
+ body {
163
+ height: 100%;
164
+ margin: 0;
165
+ overflow: hidden;
166
+ background: transparent;
167
+ }
168
+
169
+ body {
170
+ color: var(--text);
171
+ /* WebKit paints the native caret using this line box; extra leading stretches it above the text. */
172
+ font:
173
+ 1rem/normal -apple-system,
174
+ BlinkMacSystemFont,
175
+ system-ui,
176
+ sans-serif;
177
+ -webkit-font-smoothing: antialiased;
178
+ -webkit-text-size-adjust: none;
179
+ }
180
+
181
+ /* The page scrolls inside #editor so the fade under the formatting bar stays put. */
182
+ #editor {
183
+ height: 100%;
184
+ overflow-y: auto;
185
+ }
186
+
187
+ .ProseMirror {
188
+ /* The viewport, not the wrapper Milkdown adds, so a click below the text still places the caret. */
189
+ min-height: 100vh;
190
+ box-sizing: border-box;
191
+ /* Scroll the final line above the floating footer without reserving a solid row. */
192
+ /* Keep the editor full-width for hit testing; center only the writing column. */
193
+ padding: 10px max(16px, calc((100% - var(--reading-width, 100%)) / 2 + 16px))
194
+ 48px;
195
+ outline: none;
196
+ white-space: pre-wrap;
197
+ word-wrap: break-word;
198
+ caret-color: var(--accent);
199
+ cursor: text;
200
+ }
201
+
202
+ .ProseMirror .empty {
203
+ position: relative;
204
+ }
205
+
206
+ .ProseMirror .empty::before {
207
+ content: attr(data-placeholder);
208
+ position: absolute;
209
+ color: var(--muted);
210
+ pointer-events: none;
211
+ }
212
+
213
+ /* The selection plugin draws the selection over the text alone; the engine's own fills the margins too. */
214
+ .ProseMirror ::selection {
215
+ background: transparent;
216
+ }
217
+
218
+ ::highlight(selection) {
219
+ background-color: color-mix(in srgb, var(--accent) 30%, transparent);
220
+ }
221
+
222
+ .ProseMirror > :first-child {
223
+ margin-top: 0;
224
+ }
225
+
226
+ .ProseMirror p {
227
+ margin: 0 0 0.667rem;
228
+ }
229
+
230
+ .ProseMirror h1,
231
+ .ProseMirror h2,
232
+ .ProseMirror h3 {
233
+ line-height: 1.25;
234
+ letter-spacing: -0.01em;
235
+ margin: 1.867rem 0 0.533rem;
236
+ }
237
+
238
+ .ProseMirror h1 {
239
+ font-size: 1.733rem;
240
+ font-weight: 700;
241
+ }
242
+
243
+ .ProseMirror h2 {
244
+ font-size: 1.4rem;
245
+ font-weight: 700;
246
+ }
247
+
248
+ .ProseMirror h3 {
249
+ font-size: 1.133rem;
250
+ font-weight: 600;
251
+ }
252
+
253
+ .ProseMirror h4,
254
+ .ProseMirror h5,
255
+ .ProseMirror h6 {
256
+ font-size: 1rem;
257
+ font-weight: 600;
258
+ margin: 1.333rem 0 0.4rem;
259
+ }
260
+
261
+ .ProseMirror strong {
262
+ font-weight: 700;
263
+ }
264
+
265
+ .ProseMirror a {
266
+ color: var(--accent);
267
+ text-decoration: none;
268
+ text-underline-offset: 2px;
269
+ }
270
+
271
+ .ProseMirror a:hover {
272
+ text-decoration: underline;
273
+ }
274
+
275
+ .meta .ProseMirror a {
276
+ cursor: pointer;
277
+ }
278
+
279
+ .ProseMirror code {
280
+ font-family: var(--mono);
281
+ font-size: 0.88em;
282
+ background: var(--code-bg);
283
+ border-radius: 5px;
284
+ padding: 0.067rem 0.333rem;
285
+ }
286
+
287
+ .ProseMirror pre {
288
+ position: relative;
289
+ font-family: var(--mono);
290
+ font-size: 0.867rem;
291
+ line-height: 1.5;
292
+ background: var(--code-bg);
293
+ border-radius: 8px;
294
+ padding: 0.8rem 0.933rem;
295
+ margin: 0 0 0.8rem;
296
+ overflow-x: auto;
297
+ }
298
+
299
+ .ProseMirror pre[data-language] {
300
+ padding-top: max(2rem, 30px);
301
+ }
302
+
303
+ .ProseMirror pre[data-language]::before {
304
+ content: attr(data-language);
305
+ position: absolute;
306
+ top: 0.533rem;
307
+ left: 0.933rem;
308
+ font:
309
+ 0.733rem/1 -apple-system,
310
+ system-ui,
311
+ sans-serif;
312
+ color: var(--muted);
313
+ text-transform: lowercase;
314
+ user-select: none;
315
+ }
316
+
317
+ .ProseMirror pre .copy {
318
+ position: absolute;
319
+ top: 6px;
320
+ right: 6px;
321
+ display: grid;
322
+ place-items: center;
323
+ width: 26px;
324
+ height: 24px;
325
+ border: 0;
326
+ border-radius: 6px;
327
+ background: color-mix(in srgb, CanvasText 10%, transparent);
328
+ color: var(--muted);
329
+ cursor: pointer;
330
+ opacity: 0;
331
+ transition: opacity 120ms;
332
+ user-select: none;
333
+ }
334
+
335
+ .ProseMirror pre:hover .copy,
336
+ .ProseMirror pre .copy[data-copied] {
337
+ opacity: 1;
338
+ }
339
+
340
+ .ProseMirror pre .copy:hover {
341
+ color: var(--text);
342
+ }
343
+
344
+ .ProseMirror pre code {
345
+ background: none;
346
+ padding: 0;
347
+ font-size: inherit;
348
+ }
349
+
350
+ .ProseMirror blockquote {
351
+ margin: 0 0 0.8rem;
352
+ padding: 2px 0 2px 0.8rem;
353
+ border-left: 2px solid var(--accent);
354
+ }
355
+
356
+ .ProseMirror blockquote > :first-child {
357
+ margin-top: 0;
358
+ }
359
+
360
+ .ProseMirror blockquote > :last-child {
361
+ margin-bottom: 0;
362
+ }
363
+
364
+ .ProseMirror hr {
365
+ border: 0;
366
+ border-top: 1px solid var(--rule);
367
+ margin: 1.333rem 0;
368
+ }
369
+
370
+ .ProseMirror ul,
371
+ .ProseMirror ol {
372
+ margin: 0 0 0.667rem;
373
+ padding-left: 1.733rem;
374
+ }
375
+
376
+ .ProseMirror li {
377
+ margin: 0 0 0.267rem;
378
+ }
379
+
380
+ .ProseMirror li > p {
381
+ margin: 0;
382
+ }
383
+
384
+ .ProseMirror li ul,
385
+ .ProseMirror li ol {
386
+ margin-top: 0.267rem;
387
+ margin-bottom: 0;
388
+ }
389
+
390
+ .ProseMirror ul > li::marker,
391
+ .ProseMirror ol > li::marker {
392
+ color: var(--accent);
393
+ }
394
+
395
+ .ProseMirror li[data-item-type='task'] {
396
+ list-style: none;
397
+ position: relative;
398
+ }
399
+
400
+ .ProseMirror li[data-item-type='task']::before {
401
+ content: '';
402
+ position: absolute;
403
+ left: -1.667rem;
404
+ top: 0.32em;
405
+ width: 0.933rem;
406
+ height: 0.933rem;
407
+ box-sizing: border-box;
408
+ border: 1.5px solid color-mix(in srgb, CanvasText 35%, transparent);
409
+ border-radius: 4px;
410
+ cursor: pointer;
411
+ }
412
+
413
+ .ProseMirror li[data-item-type='task'][data-checked='true']::before {
414
+ border-color: var(--accent);
415
+ background: var(--accent);
416
+ }
417
+
418
+ .ProseMirror li[data-item-type='task'][data-checked='true']::after {
419
+ content: '';
420
+ position: absolute;
421
+ left: -1.333rem;
422
+ top: 0.32em;
423
+ margin-top: 0.133rem;
424
+ width: 0.2rem;
425
+ height: 0.467rem;
426
+ border: solid Canvas;
427
+ border-width: 0 1.5px 1.5px 0;
428
+ transform: rotate(45deg);
429
+ pointer-events: none;
430
+ }
431
+
432
+ .ProseMirror li[data-item-type='task'][data-checked='true'] > p {
433
+ color: var(--muted);
434
+ }
435
+
436
+ .ProseMirror del {
437
+ color: var(--muted);
438
+ }
439
+
440
+ .ProseMirror .ProseMirror-selectednode {
441
+ outline: 2px solid color-mix(in srgb, var(--accent) 60%, transparent);
442
+ outline-offset: 2px;
443
+ border-radius: 4px;
444
+ }
445
+
446
+ /* Syntax colors: One Dark and One Light, without the blues. */
447
+ :root {
448
+ --code-comment: var(--muted);
449
+ --code-keyword: #a626a4;
450
+ --code-string: #50a14f;
451
+ --code-number: #986801;
452
+ --code-title: #c18401;
453
+ --code-attr: #e45649;
454
+ }
455
+
456
+ @media (prefers-color-scheme: dark) {
457
+ :root {
458
+ --code-keyword: #c678dd;
459
+ --code-string: #98c379;
460
+ --code-number: #d19a66;
461
+ --code-title: #e5c07b;
462
+ --code-attr: #e06c75;
463
+ }
464
+ }
465
+
466
+ .hljs-comment,
467
+ .hljs-quote {
468
+ color: var(--code-comment);
469
+ font-style: italic;
470
+ }
471
+
472
+ .hljs-keyword,
473
+ .hljs-literal,
474
+ .hljs-built_in,
475
+ .hljs-type,
476
+ .hljs-selector-tag,
477
+ .hljs-doctag {
478
+ color: var(--code-keyword);
479
+ }
480
+
481
+ .hljs-string,
482
+ .hljs-regexp,
483
+ .hljs-addition,
484
+ .hljs-template-tag {
485
+ color: var(--code-string);
486
+ }
487
+
488
+ .hljs-number,
489
+ .hljs-symbol,
490
+ .hljs-bullet,
491
+ .hljs-link,
492
+ .hljs-meta {
493
+ color: var(--code-number);
494
+ }
495
+
496
+ .hljs-title,
497
+ .hljs-section,
498
+ .hljs-name,
499
+ .hljs-selector-id,
500
+ .hljs-selector-class {
501
+ color: var(--code-title);
502
+ }
503
+
504
+ .hljs-attr,
505
+ .hljs-attribute,
506
+ .hljs-property,
507
+ .hljs-variable,
508
+ .hljs-template-variable,
509
+ .hljs-tag,
510
+ .hljs-deletion {
511
+ color: var(--code-attr);
512
+ }
513
+
514
+ .hljs-emphasis {
515
+ font-style: italic;
516
+ }
517
+
518
+ .hljs-strong {
519
+ font-weight: 700;
520
+ }
521
+
522
+ [hidden] {
523
+ display: none !important;
524
+ }
525
+ .ProseMirror-search-match {
526
+ background: color-mix(in srgb, var(--accent) 25%, transparent);
527
+ }
528
+
529
+ .ProseMirror-active-search-match {
530
+ background: color-mix(in srgb, var(--accent) 55%, transparent);
531
+ }
532
+
533
+ .ProseMirror .literal-markdown {
534
+ white-space: pre-wrap;
535
+ font-family: var(--mono);
536
+ font-size: 0.9rem;
537
+ }
@@ -0,0 +1,7 @@
1
+ import type { Ctx } from '@milkdown/kit/ctx';
2
+ export type TableCommand = 'insert' | 'addRowBefore' | 'addRowAfter' | 'addColumnBefore' | 'addColumnAfter' | 'deleteRow' | 'deleteColumn' | 'deleteTable' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'exit';
3
+ export declare const tablePlugins: (((ctx: Ctx) => () => void) | import("@milkdown/kit/ctx").MilkdownPlugin)[];
4
+ export declare function tableCommand(ctx: Ctx, command: TableCommand, options?: {
5
+ rows?: number;
6
+ columns?: number;
7
+ }): boolean;
package/dist/tables.js ADDED
@@ -0,0 +1,80 @@
1
+ import { commandsCtx, editorViewCtx } from '@milkdown/kit/core';
2
+ import { tableBlock, tableBlockConfig, } from '@milkdown/kit/component/table-block';
3
+ import { insertTableCommand, exitTable } from '@milkdown/kit/preset/gfm';
4
+ import { addRowAfter, addRowBefore, addColumnAfter, addColumnBefore, deleteRow, deleteColumn, deleteTable, goToNextCell, selectedRect, TableMap, isInTable, } from '@milkdown/kit/prose/tables';
5
+ import { keymap } from '@milkdown/kit/prose/keymap';
6
+ import { $prose } from '@milkdown/kit/utils';
7
+ const labels = {
8
+ add_row: 'Add row',
9
+ add_col: 'Add column',
10
+ delete_row: 'Delete row',
11
+ delete_col: 'Delete column',
12
+ align_col_left: 'Align left',
13
+ align_col_center: 'Align center',
14
+ align_col_right: 'Align right',
15
+ col_drag_handle: 'Move column',
16
+ row_drag_handle: 'Move row',
17
+ };
18
+ export const tablePlugins = [
19
+ tableBlockConfig,
20
+ (ctx) => () => {
21
+ ctx.update(tableBlockConfig.key, (config) => ({
22
+ ...config,
23
+ renderButton: (type) => `<span role="img" aria-label="${labels[type]}">${labels[type]}</span>`,
24
+ }));
25
+ },
26
+ tableBlock,
27
+ $prose(() => keymap({ Tab: goToNextCell(1), 'Shift-Tab': goToNextCell(-1) })),
28
+ ].flat();
29
+ export function tableCommand(ctx, command, options) {
30
+ const view = ctx.get(editorViewCtx);
31
+ const commands = ctx.get(commandsCtx);
32
+ if (command === 'insert') {
33
+ const row = options?.rows ?? 3, col = options?.columns ?? 2;
34
+ if (!Number.isInteger(row) ||
35
+ !Number.isInteger(col) ||
36
+ row < 1 ||
37
+ row > 100 ||
38
+ col < 1 ||
39
+ col > 100)
40
+ throw new RangeError('Table dimensions must be integers from 1 to 100');
41
+ return commands.call(insertTableCommand.key, { row, col });
42
+ }
43
+ if (command === 'exit')
44
+ return commands.call(exitTable.key);
45
+ if (command === 'alignLeft' ||
46
+ command === 'alignCenter' ||
47
+ command === 'alignRight') {
48
+ if (!isInTable(view.state))
49
+ return false;
50
+ const rect = selectedRect(view.state), map = TableMap.get(rect.table);
51
+ const alignment = command === 'alignLeft'
52
+ ? 'left'
53
+ : command === 'alignRight'
54
+ ? 'right'
55
+ : 'center';
56
+ const tr = view.state.tr;
57
+ // GFM stores alignment on the header; apply it to the entire column so
58
+ // editing a body cell does not disappear when Markdown is saved.
59
+ const positions = new Set();
60
+ for (let row = 0; row < map.height; row++)
61
+ for (let col = rect.left; col < rect.right; col++)
62
+ positions.add(map.map[row * map.width + col]);
63
+ for (const offset of positions) {
64
+ const pos = rect.tableStart + offset, cell = tr.doc.nodeAt(pos);
65
+ tr.setNodeMarkup(pos, undefined, { ...cell.attrs, alignment });
66
+ }
67
+ view.dispatch(tr);
68
+ return true;
69
+ }
70
+ const actions = {
71
+ addRowBefore,
72
+ addRowAfter,
73
+ addColumnBefore,
74
+ addColumnAfter,
75
+ deleteRow,
76
+ deleteColumn,
77
+ deleteTable,
78
+ };
79
+ return actions[command](view.state, view.dispatch);
80
+ }
@@ -0,0 +1,3 @@
1
+ import type { Ctx } from '@milkdown/kit/ctx';
2
+ export declare const taskListPlugin: import("@milkdown/kit/utils").$Prose;
3
+ export declare function toggleTaskList(ctx: Ctx): void;
package/dist/tasks.js ADDED
@@ -0,0 +1,49 @@
1
+ import { commandsCtx, editorViewCtx } from '@milkdown/kit/core';
2
+ import { wrapInBulletListCommand } from '@milkdown/kit/preset/commonmark';
3
+ import { Plugin, PluginKey } from '@milkdown/kit/prose/state';
4
+ import { $prose } from '@milkdown/kit/utils';
5
+ // A click on the task marker lands on the <li> itself, since the marker is a
6
+ // pseudo-element; clicks inside the item's text land on its children.
7
+ export const taskListPlugin = $prose(() => new Plugin({
8
+ key: new PluginKey('taskToggle'),
9
+ props: {
10
+ handleClickOn(view, _pos, node, nodePos, event) {
11
+ if (node.type.name !== 'list_item' || node.attrs.checked == null)
12
+ return false;
13
+ const target = event.target;
14
+ if (!target.matches('li[data-item-type="task"]'))
15
+ return false;
16
+ view.dispatch(view.state.tr.setNodeMarkup(nodePos, undefined, {
17
+ ...node.attrs,
18
+ checked: !node.attrs.checked,
19
+ }));
20
+ return true;
21
+ },
22
+ },
23
+ }));
24
+ function listItemsInSelection(view) {
25
+ const { from, to } = view.state.selection;
26
+ const items = [];
27
+ view.state.doc.nodesBetween(from, to, (node, pos) => {
28
+ if (node.type.name === 'list_item')
29
+ items.push({ pos, node });
30
+ });
31
+ return items;
32
+ }
33
+ export function toggleTaskList(ctx) {
34
+ const view = ctx.get(editorViewCtx);
35
+ let items = listItemsInSelection(view);
36
+ if (items.length === 0) {
37
+ ctx.get(commandsCtx).call(wrapInBulletListCommand.key);
38
+ items = listItemsInSelection(view);
39
+ }
40
+ const allTasks = items.every(({ node }) => node.attrs.checked != null);
41
+ let tr = view.state.tr;
42
+ for (const { pos, node } of items) {
43
+ tr = tr.setNodeMarkup(pos, undefined, {
44
+ ...node.attrs,
45
+ checked: allTasks ? null : (node.attrs.checked ?? false),
46
+ });
47
+ }
48
+ view.dispatch(tr);
49
+ }