@bendyline/squisq-editor-react 2.0.1 → 2.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/README.md +10 -2
- package/dist/index.d.ts +505 -15
- package/dist/index.js +6035 -2283
- package/dist/index.js.map +1 -1
- package/dist/styles/index.css +870 -40
- package/package.json +6 -5
- package/src/EditorContext.tsx +27 -0
- package/src/EditorShell.tsx +24 -0
- package/src/PreviewControls.tsx +20 -5
- package/src/PreviewPanel.tsx +5 -1
- package/src/RawEditor.tsx +12 -0
- package/src/RecorderEntry.tsx +3 -0
- package/src/Toolbar.tsx +315 -17
- package/src/WysiwygEditor.tsx +25 -11
- package/src/__tests__/buildPreviewDocContent.test.ts +17 -0
- package/src/__tests__/editorShellProps.test.tsx +65 -0
- package/src/__tests__/findMode.test.tsx +104 -0
- package/src/__tests__/findModel.test.ts +55 -0
- package/src/__tests__/markdownCodeFence.test.ts +45 -0
- package/src/__tests__/mediaReferences.test.ts +15 -0
- package/src/__tests__/previewControls.test.tsx +31 -0
- package/src/__tests__/tiptapBridge.test.ts +9 -0
- package/src/__tests__/toolbarSelectionConversion.test.tsx +26 -0
- package/src/__tests__/writeCanvasSettings.test.ts +15 -0
- package/src/asciiDiagram/__tests__/AsciiDiagramExtension.test.ts +20 -1
- package/src/buildPreviewDoc.ts +9 -7
- package/src/codeSnippet/CodeSnippetExtension.ts +205 -0
- package/src/codeSnippet/CodeSnippetWidget.tsx +109 -0
- package/src/codeSnippet/__tests__/CodeSnippetExtension.test.ts +95 -0
- package/src/codeSnippet/__tests__/codeSnippetLanguages.test.ts +50 -0
- package/src/codeSnippet/codeSnippetCommands.ts +21 -0
- package/src/codeSnippet/codeSnippetData.ts +43 -0
- package/src/codeSnippet/codeSnippetLanguages.ts +216 -0
- package/src/diagram/DiagramCanvas.tsx +1 -0
- package/src/find/FindHighlightExtension.ts +81 -0
- package/src/find/FindToolbar.tsx +314 -0
- package/src/find/findModel.ts +77 -0
- package/src/index.ts +89 -0
- package/src/markdownCodeFence.ts +72 -0
- package/src/mediaReferences.ts +5 -3
- package/src/mermaid/MermaidDiagramCanvas.tsx +693 -0
- package/src/mermaid/MermaidDiagramExtension.ts +243 -0
- package/src/mermaid/MermaidDiagramTypeThumbnail.tsx +184 -0
- package/src/mermaid/MermaidDiagramWidget.tsx +653 -0
- package/src/mermaid/MermaidShapePalette.tsx +245 -0
- package/src/mermaid/__tests__/MermaidDiagramExtension.test.ts +361 -0
- package/src/mermaid/__tests__/mermaidDiagramTypes.test.ts +35 -0
- package/src/mermaid/__tests__/mermaidRenderer.test.ts +49 -0
- package/src/mermaid/__tests__/mermaidSourceOps.test.ts +213 -0
- package/src/mermaid/__tests__/mermaidSyntax.test.ts +71 -0
- package/src/mermaid/mermaidCommands.ts +34 -0
- package/src/mermaid/mermaidData.ts +31 -0
- package/src/mermaid/mermaidDiagramTypes.ts +325 -0
- package/src/mermaid/mermaidModel.ts +31 -0
- package/src/mermaid/mermaidRenderer.ts +181 -0
- package/src/mermaid/mermaidShapes.ts +113 -0
- package/src/mermaid/mermaidSourceOps.ts +454 -0
- package/src/scene/Scene.tsx +46 -15
- package/src/scene/SceneBlockToolbar.tsx +29 -14
- package/src/scene/SceneViewControls.tsx +43 -0
- package/src/scene/__tests__/fitScale.test.ts +19 -0
- package/src/scene/__tests__/useScenePanZoom.test.ts +28 -1
- package/src/scene/fitScale.ts +17 -0
- package/src/scene/hooks/useScenePanZoom.ts +11 -4
- package/src/scene/scene.css +85 -3
- package/src/styles/code-snippet.css +76 -0
- package/src/styles/editor.css +322 -2
- package/src/styles/index.css +2 -0
- package/src/styles/mermaid-diagram.css +487 -0
- package/src/writeCanvasSettings.ts +30 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { calculateFitScale } from '../fitScale';
|
|
3
|
+
|
|
4
|
+
describe('calculateFitScale', () => {
|
|
5
|
+
it('does not enlarge content that already fits', () => {
|
|
6
|
+
expect(calculateFitScale({ width: 200, height: 100 }, { width: 800, height: 600 })).toBe(1);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('uses the smaller axis scale so every shape remains visible', () => {
|
|
10
|
+
expect(calculateFitScale({ width: 1_000, height: 200 }, { width: 500, height: 300 })).toBe(0.5);
|
|
11
|
+
expect(calculateFitScale({ width: 200, height: 1_000 }, { width: 500, height: 250 })).toBe(
|
|
12
|
+
0.25,
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('falls back to 100% until dimensions are measurable', () => {
|
|
17
|
+
expect(calculateFitScale({ width: 0, height: 100 }, { width: 500, height: 300 })).toBe(1);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -44,7 +44,7 @@ describe('useScenePanZoom', () => {
|
|
|
44
44
|
act(() => result.current.zoomAt(100, 0, 0));
|
|
45
45
|
expect(result.current.transform.scale).toBeLessThanOrEqual(8);
|
|
46
46
|
act(() => result.current.zoomAt(0.0001, 0, 0));
|
|
47
|
-
expect(result.current.transform.scale).toBeGreaterThanOrEqual(0.
|
|
47
|
+
expect(result.current.transform.scale).toBeGreaterThanOrEqual(0.02);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
it('reset returns to identity', () => {
|
|
@@ -88,6 +88,33 @@ describe('useScenePanZoom', () => {
|
|
|
88
88
|
expect(result.current.transform.scale).toBeCloseTo(1.6, 5);
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
+
it('fitBox can cap fit at 100% without enlarging small content', () => {
|
|
92
|
+
const { result } = renderHook(() => useScenePanZoom());
|
|
93
|
+
act(() =>
|
|
94
|
+
result.current.fitBox(
|
|
95
|
+
{ x: 25, y: 50, width: 100, height: 80 },
|
|
96
|
+
{ width: 800, height: 600 },
|
|
97
|
+
20,
|
|
98
|
+
1,
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
expect(result.current.transform.scale).toBe(1);
|
|
102
|
+
expect(result.current.viewportToScreen(75, 90)).toEqual({ x: 400, y: 300 });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('fitBox may shrink below the manual zoom floor to keep all content visible', () => {
|
|
106
|
+
const { result } = renderHook(() => useScenePanZoom());
|
|
107
|
+
act(() =>
|
|
108
|
+
result.current.fitBox(
|
|
109
|
+
{ x: 0, y: 0, width: 10_000, height: 10_000 },
|
|
110
|
+
{ width: 400, height: 300 },
|
|
111
|
+
20,
|
|
112
|
+
1,
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
expect(result.current.transform.scale).toBeCloseTo(0.026, 5);
|
|
116
|
+
});
|
|
117
|
+
|
|
91
118
|
it('screenToViewport and viewportToScreen are inverses', () => {
|
|
92
119
|
const { result } = renderHook(() => useScenePanZoom());
|
|
93
120
|
act(() => {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface SceneSize {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Scale content so it is wholly visible, without enlarging it past the
|
|
8
|
+
* supplied maximum (100% by default). Invalid or not-yet-measured dimensions
|
|
9
|
+
* fall back to 100%.
|
|
10
|
+
*/
|
|
11
|
+
export function calculateFitScale(content: SceneSize, container: SceneSize, maxScale = 1): number {
|
|
12
|
+
if (content.width <= 0 || content.height <= 0 || container.width <= 0 || container.height <= 0) {
|
|
13
|
+
return Math.min(1, maxScale);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return Math.min(maxScale, container.width / content.width, container.height / content.height);
|
|
17
|
+
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { useCallback, useRef, useState } from 'react';
|
|
16
|
+
import { calculateFitScale } from '../fitScale';
|
|
16
17
|
|
|
17
18
|
export interface SceneTransform {
|
|
18
19
|
/** Translation in screen pixels. */
|
|
@@ -25,7 +26,7 @@ export interface SceneTransform {
|
|
|
25
26
|
export const IDENTITY_TRANSFORM: SceneTransform = Object.freeze({ tx: 0, ty: 0, scale: 1 });
|
|
26
27
|
|
|
27
28
|
/** Clamp scale to a sensible range so a single accidental wheel doesn't escape. */
|
|
28
|
-
const MIN_SCALE = 0.
|
|
29
|
+
const MIN_SCALE = 0.02;
|
|
29
30
|
const MAX_SCALE = 8;
|
|
30
31
|
|
|
31
32
|
export interface UseScenePanZoomResult {
|
|
@@ -45,13 +46,14 @@ export interface UseScenePanZoomResult {
|
|
|
45
46
|
reset: () => void;
|
|
46
47
|
/**
|
|
47
48
|
* Fit a viewport-unit bounding box into a container of `containerSize`
|
|
48
|
-
* screen pixels, with optional padding (also in screen pixels)
|
|
49
|
-
* the box is empty, no-ops.
|
|
49
|
+
* screen pixels, with optional padding (also in screen pixels) and an
|
|
50
|
+
* optional maximum scale. When the box is empty, no-ops.
|
|
50
51
|
*/
|
|
51
52
|
fitBox: (
|
|
52
53
|
box: { x: number; y: number; width: number; height: number },
|
|
53
54
|
containerSize: { width: number; height: number },
|
|
54
55
|
padding?: number,
|
|
56
|
+
maxScale?: number,
|
|
55
57
|
) => void;
|
|
56
58
|
/** Convert a screen-pixel point (container-relative) → viewport units. */
|
|
57
59
|
screenToViewport: (sx: number, sy: number) => { x: number; y: number };
|
|
@@ -104,12 +106,17 @@ export function useScenePanZoom(
|
|
|
104
106
|
box: { x: number; y: number; width: number; height: number },
|
|
105
107
|
containerSize: { width: number; height: number },
|
|
106
108
|
padding = 24,
|
|
109
|
+
maxScale = MAX_SCALE,
|
|
107
110
|
) => {
|
|
108
111
|
if (box.width <= 0 || box.height <= 0) return;
|
|
109
112
|
if (containerSize.width <= 0 || containerSize.height <= 0) return;
|
|
110
113
|
const availW = Math.max(1, containerSize.width - padding * 2);
|
|
111
114
|
const availH = Math.max(1, containerSize.height - padding * 2);
|
|
112
|
-
const scale =
|
|
115
|
+
const scale = calculateFitScale(
|
|
116
|
+
{ width: box.width, height: box.height },
|
|
117
|
+
{ width: availW, height: availH },
|
|
118
|
+
Math.min(MAX_SCALE, Math.max(Number.EPSILON, maxScale)),
|
|
119
|
+
);
|
|
113
120
|
// Center the box in the container.
|
|
114
121
|
const scaledW = box.width * scale;
|
|
115
122
|
const scaledH = box.height * scale;
|
package/src/scene/scene.css
CHANGED
|
@@ -266,6 +266,18 @@
|
|
|
266
266
|
border-color: #cbd5e1;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
.squisq-scene-action--active,
|
|
270
|
+
.squisq-scene-action--active:hover:not(:disabled) {
|
|
271
|
+
background: #e0e7ff;
|
|
272
|
+
border-color: #4f46e5;
|
|
273
|
+
color: #4338ca;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.squisq-scene-action-popover-anchor {
|
|
277
|
+
position: relative;
|
|
278
|
+
display: inline-flex;
|
|
279
|
+
}
|
|
280
|
+
|
|
269
281
|
.squisq-scene-action:focus-visible {
|
|
270
282
|
outline: 2px solid #6366f1;
|
|
271
283
|
outline-offset: 1px;
|
|
@@ -322,7 +334,9 @@
|
|
|
322
334
|
}
|
|
323
335
|
|
|
324
336
|
.squisq-editor-shell[data-theme='dark'] .squisq-scene-tool--active,
|
|
325
|
-
.squisq-editor-shell[data-theme='dark'] .squisq-scene-tool--active:hover
|
|
337
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-scene-tool--active:hover,
|
|
338
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-scene-action--active,
|
|
339
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-scene-action--active:hover:not(:disabled) {
|
|
326
340
|
background: #312e81;
|
|
327
341
|
border-color: #818cf8;
|
|
328
342
|
color: #f8fafc;
|
|
@@ -350,7 +364,8 @@
|
|
|
350
364
|
padding: 12px;
|
|
351
365
|
}
|
|
352
366
|
.squisq-scene-block-max > .squisq-diagram-canvas,
|
|
353
|
-
.squisq-scene-block-max > .squisq-scene-stage
|
|
367
|
+
.squisq-scene-block-max > .squisq-scene-stage,
|
|
368
|
+
.squisq-scene-block-max > .squisq-mermaid-canvas {
|
|
354
369
|
flex: 1;
|
|
355
370
|
min-width: 0;
|
|
356
371
|
min-height: 0;
|
|
@@ -403,6 +418,12 @@
|
|
|
403
418
|
right: calc(100% + 6px);
|
|
404
419
|
}
|
|
405
420
|
|
|
421
|
+
.squisq-scene-side-toolbar .squisq-scene-action-popover-anchor > [role='dialog'] {
|
|
422
|
+
top: 0;
|
|
423
|
+
left: auto;
|
|
424
|
+
right: calc(100% + 6px);
|
|
425
|
+
}
|
|
426
|
+
|
|
406
427
|
/* ── Narrow-width fallback bar ─────────────────────────────────
|
|
407
428
|
When SceneSideToolbar measures that the right gutter can't hold the 180px
|
|
408
429
|
column, it renders the same toolbar as an in-flow horizontal bar above the
|
|
@@ -420,7 +441,68 @@
|
|
|
420
441
|
margin: 0;
|
|
421
442
|
}
|
|
422
443
|
|
|
423
|
-
/* ──
|
|
444
|
+
/* ── View controls + maximize ─────────────────────────────── */
|
|
445
|
+
|
|
446
|
+
.squisq-scene-view-controls {
|
|
447
|
+
position: absolute;
|
|
448
|
+
top: 8px;
|
|
449
|
+
left: 8px;
|
|
450
|
+
z-index: 10;
|
|
451
|
+
display: inline-flex;
|
|
452
|
+
align-items: center;
|
|
453
|
+
overflow: hidden;
|
|
454
|
+
border: 1px solid var(--squisq-border, #e2e8f0);
|
|
455
|
+
border-radius: 6px;
|
|
456
|
+
background: var(--squisq-surface, #ffffff);
|
|
457
|
+
color: var(--squisq-text, #1e293b);
|
|
458
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.squisq-scene-view-controls button,
|
|
462
|
+
.squisq-scene-view-scale {
|
|
463
|
+
box-sizing: border-box;
|
|
464
|
+
min-width: 32px;
|
|
465
|
+
height: 32px;
|
|
466
|
+
padding: 0 8px;
|
|
467
|
+
border: 0;
|
|
468
|
+
border-right: 1px solid var(--squisq-border, #e2e8f0);
|
|
469
|
+
background: transparent;
|
|
470
|
+
color: inherit;
|
|
471
|
+
font: inherit;
|
|
472
|
+
font-size: 12px;
|
|
473
|
+
line-height: 32px;
|
|
474
|
+
text-align: center;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.squisq-scene-view-controls button {
|
|
478
|
+
cursor: pointer;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.squisq-scene-view-controls button:last-child {
|
|
482
|
+
border-right: 0;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.squisq-scene-view-controls button:hover,
|
|
486
|
+
.squisq-scene-view-fit[data-active='true'] {
|
|
487
|
+
background: var(--squisq-surface-hover, #f1f5f9);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.squisq-scene-view-fit[data-active='true'] {
|
|
491
|
+
color: #4338ca;
|
|
492
|
+
font-weight: 600;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-scene-view-controls {
|
|
496
|
+
background: #111827;
|
|
497
|
+
border-color: #475569;
|
|
498
|
+
color: #e5e7eb;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-scene-view-controls button:hover,
|
|
502
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-scene-view-fit[data-active='true'] {
|
|
503
|
+
background: #312e81;
|
|
504
|
+
color: #f8fafc;
|
|
505
|
+
}
|
|
424
506
|
|
|
425
507
|
.squisq-scene-maximize-btn {
|
|
426
508
|
position: absolute;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* Monaco inset for ordinary explicit-language Markdown code fences. */
|
|
2
|
+
|
|
3
|
+
.squisq-wysiwyg-editor pre.squisq-code-snippet-fence-hidden {
|
|
4
|
+
display: none;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.squisq-code-snippet-widget-host {
|
|
8
|
+
margin: 0.75rem 0 1.25rem;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.squisq-code-snippet-shell {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
display: flex;
|
|
14
|
+
width: 100%;
|
|
15
|
+
height: 240px;
|
|
16
|
+
min-height: 128px;
|
|
17
|
+
max-height: 70vh;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
resize: vertical;
|
|
21
|
+
border: 1px solid var(--squisq-border, #d1d5db);
|
|
22
|
+
border-radius: 8px;
|
|
23
|
+
background: var(--squisq-surface, #ffffff);
|
|
24
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.squisq-code-snippet-header {
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
display: flex;
|
|
30
|
+
min-height: 34px;
|
|
31
|
+
align-items: center;
|
|
32
|
+
justify-content: space-between;
|
|
33
|
+
gap: 12px;
|
|
34
|
+
padding: 0 10px;
|
|
35
|
+
border-bottom: 1px solid var(--squisq-border, #d1d5db);
|
|
36
|
+
background: var(--squisq-surface-muted, #f8fafc);
|
|
37
|
+
color: var(--squisq-text-muted, #64748b);
|
|
38
|
+
font-family: var(--squisq-ux-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
|
|
39
|
+
font-size: 12px;
|
|
40
|
+
user-select: none;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.squisq-code-snippet-title {
|
|
44
|
+
display: inline-flex;
|
|
45
|
+
min-width: 0;
|
|
46
|
+
align-items: center;
|
|
47
|
+
gap: 7px;
|
|
48
|
+
color: var(--squisq-text, #334155);
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.squisq-code-snippet-header code {
|
|
53
|
+
overflow: hidden;
|
|
54
|
+
color: inherit;
|
|
55
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
|
|
56
|
+
text-overflow: ellipsis;
|
|
57
|
+
white-space: nowrap;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.squisq-code-snippet-editor {
|
|
61
|
+
position: relative;
|
|
62
|
+
min-height: 0;
|
|
63
|
+
flex: 1;
|
|
64
|
+
overflow: hidden;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.squisq-code-snippet-loading {
|
|
68
|
+
display: flex;
|
|
69
|
+
width: 100%;
|
|
70
|
+
height: 100%;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
color: var(--squisq-text-muted, #64748b);
|
|
74
|
+
font-family: var(--squisq-ux-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
|
|
75
|
+
font-size: 12px;
|
|
76
|
+
}
|
package/src/styles/editor.css
CHANGED
|
@@ -359,6 +359,119 @@
|
|
|
359
359
|
min-width: 0;
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
+
/* Host-triggered Find mode. It occupies the toolbar's flexible middle lane,
|
|
363
|
+
leaving the view tabs on its left and the normal right-side host/settings
|
|
364
|
+
controls on its right. */
|
|
365
|
+
.squisq-find-toolbar {
|
|
366
|
+
display: flex;
|
|
367
|
+
align-items: center;
|
|
368
|
+
flex: 1 1 auto;
|
|
369
|
+
gap: 3px;
|
|
370
|
+
min-width: 180px;
|
|
371
|
+
padding: 4px 6px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.squisq-find-field {
|
|
375
|
+
display: flex;
|
|
376
|
+
align-items: center;
|
|
377
|
+
flex: 0 1 420px;
|
|
378
|
+
min-width: 130px;
|
|
379
|
+
height: 28px;
|
|
380
|
+
border: 1px solid #9ca3af;
|
|
381
|
+
border-radius: 5px;
|
|
382
|
+
background: #ffffff;
|
|
383
|
+
color: #6b7280;
|
|
384
|
+
box-sizing: border-box;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.squisq-find-field:focus-within {
|
|
388
|
+
border-color: #2563eb;
|
|
389
|
+
box-shadow: 0 0 0 1px #2563eb;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.squisq-find-field > i {
|
|
393
|
+
margin-left: 8px;
|
|
394
|
+
color: #6b7280;
|
|
395
|
+
font-size: 11px;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.squisq-find-input {
|
|
399
|
+
flex: 1 1 auto;
|
|
400
|
+
min-width: 32px;
|
|
401
|
+
height: 100%;
|
|
402
|
+
padding: 0 7px;
|
|
403
|
+
border: 0;
|
|
404
|
+
outline: 0;
|
|
405
|
+
background: transparent;
|
|
406
|
+
color: #111827;
|
|
407
|
+
font: inherit;
|
|
408
|
+
font-size: 12.5px;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.squisq-find-input::-webkit-search-cancel-button {
|
|
412
|
+
display: none;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.squisq-find-count {
|
|
416
|
+
flex: 0 0 auto;
|
|
417
|
+
padding-right: 7px;
|
|
418
|
+
color: #6b7280;
|
|
419
|
+
font-size: 11px;
|
|
420
|
+
white-space: nowrap;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.squisq-find-button {
|
|
424
|
+
display: inline-flex;
|
|
425
|
+
align-items: center;
|
|
426
|
+
justify-content: center;
|
|
427
|
+
width: 28px;
|
|
428
|
+
height: 28px;
|
|
429
|
+
padding: 0;
|
|
430
|
+
border: 0;
|
|
431
|
+
border-radius: 4px;
|
|
432
|
+
background: transparent;
|
|
433
|
+
color: var(--squisq-toolbar-icon);
|
|
434
|
+
cursor: pointer;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.squisq-find-button:hover:not(:disabled) {
|
|
438
|
+
background: rgba(0, 0, 0, 0.08);
|
|
439
|
+
color: #111827;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.squisq-find-button:disabled {
|
|
443
|
+
opacity: 0.35;
|
|
444
|
+
cursor: default;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.squisq-find-close {
|
|
448
|
+
margin-left: 2px;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/* Shared by Tiptap decorations, Monaco inline decorations, and the fallback
|
|
452
|
+
used when a preview webview lacks the CSS Custom Highlight API. */
|
|
453
|
+
.squisq-find-match {
|
|
454
|
+
border-radius: 2px;
|
|
455
|
+
background: #fde68a !important;
|
|
456
|
+
color: #111827 !important;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.squisq-find-match--selected {
|
|
460
|
+
background: #f59e0b !important;
|
|
461
|
+
box-shadow: 0 0 0 1px #b45309;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
@container squisq-toolbar (max-width: 680px) {
|
|
465
|
+
.squisq-find-toolbar {
|
|
466
|
+
min-width: 120px;
|
|
467
|
+
padding-right: 2px;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.squisq-find-count {
|
|
471
|
+
display: none;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
362
475
|
/* Contextual groups (template/transition pickers, table controls) sit at the
|
|
363
476
|
end of the actions row. When measurement marks one as clipped it moves into
|
|
364
477
|
the ··· overflow menu; hide the cropped inline original rather than painting
|
|
@@ -703,8 +816,178 @@
|
|
|
703
816
|
border-top-color: #374151;
|
|
704
817
|
}
|
|
705
818
|
|
|
819
|
+
.squisq-code-snippet-menu {
|
|
820
|
+
width: 220px;
|
|
821
|
+
max-height: min(440px, calc(100vh - 16px));
|
|
822
|
+
overflow-y: auto;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
.squisq-code-snippet-language {
|
|
826
|
+
justify-content: space-between;
|
|
827
|
+
gap: 16px;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
.squisq-code-snippet-language code {
|
|
831
|
+
overflow: hidden;
|
|
832
|
+
color: #9ca3af;
|
|
833
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
|
|
834
|
+
font-size: 11px;
|
|
835
|
+
text-overflow: ellipsis;
|
|
836
|
+
white-space: nowrap;
|
|
837
|
+
}
|
|
838
|
+
|
|
706
839
|
/* ─── Template Picker (toolbar) ──────────────────────── */
|
|
707
840
|
|
|
841
|
+
.squisq-mermaid-type-menu {
|
|
842
|
+
width: min(520px, calc(100vw - 16px));
|
|
843
|
+
max-height: min(600px, calc(100vh - 16px));
|
|
844
|
+
padding: 0 8px 10px;
|
|
845
|
+
overflow-y: auto;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
.squisq-mermaid-type-menu-heading {
|
|
849
|
+
position: sticky;
|
|
850
|
+
z-index: 1;
|
|
851
|
+
top: 0;
|
|
852
|
+
display: flex;
|
|
853
|
+
flex-direction: column;
|
|
854
|
+
gap: 2px;
|
|
855
|
+
margin: 0 -8px 4px;
|
|
856
|
+
padding: 12px 14px 10px;
|
|
857
|
+
color: #111827;
|
|
858
|
+
background: #fff;
|
|
859
|
+
border-bottom: 1px solid #e5e7eb;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.squisq-mermaid-type-menu-heading strong {
|
|
863
|
+
font-size: 14px;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
.squisq-mermaid-type-menu-heading span {
|
|
867
|
+
color: #6b7280;
|
|
868
|
+
font-size: 11px;
|
|
869
|
+
line-height: 1.35;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
.squisq-mermaid-type-section + .squisq-mermaid-type-section {
|
|
873
|
+
margin-top: 4px;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
.squisq-mermaid-type-grid {
|
|
877
|
+
display: grid;
|
|
878
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
879
|
+
gap: 6px;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.squisq-mermaid-type-card {
|
|
883
|
+
display: grid;
|
|
884
|
+
grid-template-columns: 82px minmax(0, 1fr);
|
|
885
|
+
align-items: center;
|
|
886
|
+
min-width: 0;
|
|
887
|
+
padding: 8px;
|
|
888
|
+
color: #1f2937;
|
|
889
|
+
font: inherit;
|
|
890
|
+
text-align: left;
|
|
891
|
+
background: #f9fafb;
|
|
892
|
+
border: 1px solid #e5e7eb;
|
|
893
|
+
border-radius: 7px;
|
|
894
|
+
cursor: pointer;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
.squisq-mermaid-type-card:hover,
|
|
898
|
+
.squisq-mermaid-type-card:focus-visible {
|
|
899
|
+
color: #1d4ed8;
|
|
900
|
+
background: #eff6ff;
|
|
901
|
+
border-color: #93c5fd;
|
|
902
|
+
outline: none;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.squisq-mermaid-type-thumbnail {
|
|
906
|
+
display: flex;
|
|
907
|
+
height: 48px;
|
|
908
|
+
align-items: center;
|
|
909
|
+
justify-content: center;
|
|
910
|
+
padding: 3px;
|
|
911
|
+
color: currentcolor;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
.squisq-mermaid-type-thumbnail svg {
|
|
915
|
+
display: block;
|
|
916
|
+
width: 100%;
|
|
917
|
+
height: 100%;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
.squisq-mermaid-type-copy,
|
|
921
|
+
.squisq-mermaid-type-title {
|
|
922
|
+
min-width: 0;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
.squisq-mermaid-type-copy {
|
|
926
|
+
display: flex;
|
|
927
|
+
flex-direction: column;
|
|
928
|
+
gap: 3px;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
.squisq-mermaid-type-title {
|
|
932
|
+
display: flex;
|
|
933
|
+
align-items: center;
|
|
934
|
+
gap: 5px;
|
|
935
|
+
font-size: 12px;
|
|
936
|
+
font-weight: 650;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.squisq-mermaid-type-description {
|
|
940
|
+
display: -webkit-box;
|
|
941
|
+
overflow: hidden;
|
|
942
|
+
color: #6b7280;
|
|
943
|
+
font-size: 10px;
|
|
944
|
+
line-height: 1.3;
|
|
945
|
+
-webkit-box-orient: vertical;
|
|
946
|
+
-webkit-line-clamp: 2;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
.squisq-mermaid-type-badge {
|
|
950
|
+
flex: none;
|
|
951
|
+
padding: 1px 4px;
|
|
952
|
+
color: #92400e;
|
|
953
|
+
font-size: 8px;
|
|
954
|
+
font-weight: 700;
|
|
955
|
+
letter-spacing: 0.03em;
|
|
956
|
+
text-transform: uppercase;
|
|
957
|
+
background: #fef3c7;
|
|
958
|
+
border-radius: 999px;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-menu-heading {
|
|
962
|
+
color: #f9fafb;
|
|
963
|
+
background: #1f2937;
|
|
964
|
+
border-bottom-color: #374151;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-menu-heading span,
|
|
968
|
+
.squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-description {
|
|
969
|
+
color: #9ca3af;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
.squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-card {
|
|
973
|
+
color: #e5e7eb;
|
|
974
|
+
background: #111827;
|
|
975
|
+
border-color: #374151;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-card:hover,
|
|
979
|
+
.squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-card:focus-visible {
|
|
980
|
+
color: #bfdbfe;
|
|
981
|
+
background: #1e3a5f;
|
|
982
|
+
border-color: #3b82f6;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
@media (max-width: 620px) {
|
|
986
|
+
.squisq-mermaid-type-grid {
|
|
987
|
+
grid-template-columns: 1fr;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
708
991
|
.squisq-template-picker {
|
|
709
992
|
display: flex;
|
|
710
993
|
align-items: center;
|
|
@@ -1990,6 +2273,7 @@
|
|
|
1990
2273
|
/* Body font follows the active squisq theme when one is selected; the
|
|
1991
2274
|
fallback inherits from the host page when it isn't. */
|
|
1992
2275
|
font-family: var(--squisq-theme-body-font, inherit);
|
|
2276
|
+
font-size: var(--squisq-write-text-size, inherit);
|
|
1993
2277
|
/* Explicit caret color so the blinking insertion point stays visible
|
|
1994
2278
|
even when an ancestor (e.g. a DocBlocks shell with the OS in dark
|
|
1995
2279
|
mode) inherits a near-white color into the editor. Without this
|
|
@@ -2069,6 +2353,15 @@
|
|
|
2069
2353
|
margin: 1.6em 0 0.3em;
|
|
2070
2354
|
}
|
|
2071
2355
|
|
|
2356
|
+
/* ProseMirror gives an empty text block a trailing <br> so its caret remains
|
|
2357
|
+
visible. Heading controls are siblings of the editable content span, so an
|
|
2358
|
+
uncontained break would temporarily push those controls onto a second line
|
|
2359
|
+
after a Markdown heading shortcut. Keep the break's caret line inside a
|
|
2360
|
+
zero-width inline box until the first heading character replaces it. */
|
|
2361
|
+
.squisq-heading-content:has(> br.ProseMirror-trailingBreak:only-child) {
|
|
2362
|
+
display: inline-block;
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2072
2365
|
/* The first block in the document has nothing above it — don't push the top of
|
|
2073
2366
|
the editor down with the section-break margin. (The editable ProseMirror node
|
|
2074
2367
|
itself carries the `squisq-wysiwyg-editor` class, so its blocks are direct
|
|
@@ -2078,8 +2371,10 @@
|
|
|
2078
2371
|
}
|
|
2079
2372
|
|
|
2080
2373
|
.squisq-wysiwyg-editor p {
|
|
2081
|
-
|
|
2082
|
-
|
|
2374
|
+
/* Slightly roomier paragraph separators make authored blank lines easier
|
|
2375
|
+
to scan without turning each paragraph into a separate card. */
|
|
2376
|
+
margin: 0.625em 0;
|
|
2377
|
+
line-height: var(--squisq-write-line-spacing, 1.7);
|
|
2083
2378
|
}
|
|
2084
2379
|
|
|
2085
2380
|
.squisq-wysiwyg-editor blockquote {
|
|
@@ -2745,6 +3040,31 @@
|
|
|
2745
3040
|
--squisq-toolbar-icon: #9ca3af;
|
|
2746
3041
|
}
|
|
2747
3042
|
|
|
3043
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-find-field {
|
|
3044
|
+
border-color: #4b5563;
|
|
3045
|
+
background: #111827;
|
|
3046
|
+
color: #9ca3af;
|
|
3047
|
+
}
|
|
3048
|
+
|
|
3049
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-find-field:focus-within {
|
|
3050
|
+
border-color: #60a5fa;
|
|
3051
|
+
box-shadow: 0 0 0 1px #60a5fa;
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-find-input {
|
|
3055
|
+
color: #f9fafb;
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-find-input::placeholder,
|
|
3059
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-find-count {
|
|
3060
|
+
color: #9ca3af;
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-find-button:hover:not(:disabled) {
|
|
3064
|
+
background: rgba(255, 255, 255, 0.1);
|
|
3065
|
+
color: #f9fafb;
|
|
3066
|
+
}
|
|
3067
|
+
|
|
2748
3068
|
.squisq-editor-shell[data-theme='dark'] .squisq-toolbar-view-tabs {
|
|
2749
3069
|
background: #111827;
|
|
2750
3070
|
border-right-color: rgba(255, 255, 255, 0.15);
|
package/src/styles/index.css
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
@import './image-edit-affordance.css';
|
|
13
13
|
@import './diagram.css';
|
|
14
14
|
@import './ascii-diagram.css';
|
|
15
|
+
@import './mermaid-diagram.css';
|
|
16
|
+
@import './code-snippet.css';
|
|
15
17
|
@import './tree-view.css';
|
|
16
18
|
@import './ascii-timeline.css';
|
|
17
19
|
@import '../scene/scene.css';
|