@tableslayer/ui 0.1.3 → 0.1.4
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/package.json +2 -13
- package/src/lib/components/Avatar/Avatar.svelte +82 -0
- package/src/lib/components/Avatar/AvatarFileInput.svelte +85 -0
- package/src/lib/components/Avatar/AvatarPopover.svelte +34 -0
- package/src/lib/components/Avatar/index.ts +4 -0
- package/src/lib/components/Avatar/types.ts +24 -0
- package/src/lib/components/BrushSizeSlider/BrushSizeSlider.svelte +174 -0
- package/src/lib/components/BrushSizeSlider/index.ts +1 -0
- package/src/lib/components/Button/Button.svelte +182 -0
- package/src/lib/components/Button/ConfirmActionButton.svelte +98 -0
- package/src/lib/components/Button/IconButton.svelte +121 -0
- package/src/lib/components/Button/RadioButton.svelte +93 -0
- package/src/lib/components/Button/index.ts +5 -0
- package/src/lib/components/Button/types.ts +54 -0
- package/src/lib/components/CardFan/CardFan.svelte +165 -0
- package/src/lib/components/CardFan/index.ts +2 -0
- package/src/lib/components/CardFan/types.ts +6 -0
- package/src/lib/components/CodeBlock/Code.svelte +7 -0
- package/src/lib/components/CodeBlock/CodeBlock.svelte +102 -0
- package/src/lib/components/CodeBlock/index.ts +3 -0
- package/src/lib/components/CodeBlock/types.ts +10 -0
- package/src/lib/components/ColorMode/ColorMode.svelte +8 -0
- package/src/lib/components/ColorMode/index.ts +2 -0
- package/src/lib/components/ColorMode/types.ts +12 -0
- package/src/lib/components/ColorPicker/ColorPicker.svelte +838 -0
- package/src/lib/components/ColorPicker/ColorPickerSwatch.svelte +32 -0
- package/src/lib/components/ColorPicker/index.ts +3 -0
- package/src/lib/components/ColorPicker/types.ts +51 -0
- package/src/lib/components/ContextMenu/ContextMenu.svelte +86 -0
- package/src/lib/components/ContextMenu/index.ts +2 -0
- package/src/lib/components/ContextMenu/types.ts +15 -0
- package/src/lib/components/DrawingSliders/DrawingSliders.svelte +379 -0
- package/src/lib/components/DrawingSliders/index.ts +1 -0
- package/src/lib/components/Editor/Editor.svelte +825 -0
- package/src/lib/components/Editor/index.ts +1 -0
- package/src/lib/components/FogSliders/FogSliders.svelte +33 -0
- package/src/lib/components/FogSliders/index.ts +1 -0
- package/src/lib/components/Hr/Hr.svelte +15 -0
- package/src/lib/components/Hr/index.ts +1 -0
- package/src/lib/components/Icon/Icon.svelte +6 -0
- package/src/lib/components/Icon/index.ts +2 -0
- package/src/lib/components/Icon/types.ts +20 -0
- package/src/lib/components/Input/DualInputSlider.svelte +126 -0
- package/src/lib/components/Input/FileInput.svelte +176 -0
- package/src/lib/components/Input/FormControl.svelte +150 -0
- package/src/lib/components/Input/FormError.svelte +37 -0
- package/src/lib/components/Input/Input.svelte +56 -0
- package/src/lib/components/Input/InputCheckbox.svelte +99 -0
- package/src/lib/components/Input/InputSlider.svelte +86 -0
- package/src/lib/components/Input/Label.svelte +19 -0
- package/src/lib/components/Input/index.ts +9 -0
- package/src/lib/components/Input/types.ts +39 -0
- package/src/lib/components/Link/Link.svelte +41 -0
- package/src/lib/components/Link/LinkBox.svelte +20 -0
- package/src/lib/components/Link/LinkOverlay.svelte +23 -0
- package/src/lib/components/Link/index.ts +4 -0
- package/src/lib/components/Link/types.ts +17 -0
- package/src/lib/components/Loading/Loader.svelte +60 -0
- package/src/lib/components/Loading/Skeleton.svelte +9 -0
- package/src/lib/components/Loading/index.ts +2 -0
- package/src/lib/components/Logo/Logo.svelte +16 -0
- package/src/lib/components/Logo/index.ts +1 -0
- package/src/lib/components/MarkerTooltip/MarkerTooltip.svelte +435 -0
- package/src/lib/components/MarkerTooltip/index.ts +1 -0
- package/src/lib/components/Menu/SelectorMenu.svelte +280 -0
- package/src/lib/components/Menu/index.ts +2 -0
- package/src/lib/components/Menu/types.ts +17 -0
- package/src/lib/components/MyCounterButton.svelte +11 -0
- package/src/lib/components/Panel/index.ts +2 -0
- package/src/lib/components/Panel/panel.svelte +18 -0
- package/src/lib/components/Panel/types.ts +8 -0
- package/src/lib/components/PersistButton/PersistButton.svelte +100 -0
- package/src/lib/components/PersistButton/index.ts +1 -0
- package/src/lib/components/Popover/Popover.svelte +81 -0
- package/src/lib/components/Popover/index.ts +2 -0
- package/src/lib/components/Popover/types.ts +19 -0
- package/src/lib/components/PropsTable/PropsTable.svelte +107 -0
- package/src/lib/components/RadialMenu/EffectPreview.svelte +36 -0
- package/src/lib/components/RadialMenu/EffectPreviewScene.svelte +194 -0
- package/src/lib/components/RadialMenu/RadialMenu.svelte +503 -0
- package/src/lib/components/RadialMenu/RadialMenuItem.svelte +176 -0
- package/src/lib/components/RadialMenu/index.ts +2 -0
- package/src/lib/components/RadialMenu/types.ts +35 -0
- package/src/lib/components/Select/Select.svelte +342 -0
- package/src/lib/components/Select/index.ts +2 -0
- package/src/lib/components/Select/types.ts +22 -0
- package/src/lib/components/Spacer/Spacer.svelte +14 -0
- package/src/lib/components/Spacer/index.ts +2 -0
- package/src/lib/components/Spacer/types.ts +5 -0
- package/src/lib/components/Stage/components/AnnotationLayer/AnnotationLayer.svelte +445 -0
- package/src/lib/components/Stage/components/AnnotationLayer/AnnotationMaterial.svelte +167 -0
- package/src/lib/components/Stage/components/AnnotationLayer/types.ts +196 -0
- package/src/lib/components/Stage/components/CursorLayer/CursorLayer.svelte +148 -0
- package/src/lib/components/Stage/components/CursorLayer/cursor.svg +26 -0
- package/src/lib/components/Stage/components/CursorLayer/index.ts +2 -0
- package/src/lib/components/Stage/components/CursorLayer/types.ts +23 -0
- package/src/lib/components/Stage/components/DrawingLayer/DrawingMaterial.svelte +364 -0
- package/src/lib/components/Stage/components/DrawingLayer/types.ts +65 -0
- package/src/lib/components/Stage/components/EdgeOverlayLayer/EdgeOverlayLayer.svelte +72 -0
- package/src/lib/components/Stage/components/EdgeOverlayLayer/types.ts +34 -0
- package/src/lib/components/Stage/components/FogLayer/FogLayer.svelte +75 -0
- package/src/lib/components/Stage/components/FogLayer/types.ts +51 -0
- package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarLayer.svelte +249 -0
- package/src/lib/components/Stage/components/FogOfWarLayer/FogOfWarMaterial.svelte +200 -0
- package/src/lib/components/Stage/components/FogOfWarLayer/types.ts +116 -0
- package/src/lib/components/Stage/components/GridLayer/GridLayer.svelte +20 -0
- package/src/lib/components/Stage/components/GridLayer/GridMaterial.svelte +69 -0
- package/src/lib/components/Stage/components/GridLayer/types.ts +79 -0
- package/src/lib/components/Stage/components/LayerInput/LayerInput.svelte +300 -0
- package/src/lib/components/Stage/components/MapLayer/MapLayer.svelte +196 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/GifDataSource.ts +265 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/IMapDataSource.ts +55 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/ImageDataSource.ts +87 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/VideoDataSource.ts +150 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/dataSourceFactory.ts +48 -0
- package/src/lib/components/Stage/components/MapLayer/dataSources/index.ts +16 -0
- package/src/lib/components/Stage/components/MapLayer/types.ts +58 -0
- package/src/lib/components/Stage/components/MarkerLayer/MarkerLayer.svelte +398 -0
- package/src/lib/components/Stage/components/MarkerLayer/MarkerToken.svelte +262 -0
- package/src/lib/components/Stage/components/MarkerLayer/types.ts +126 -0
- package/src/lib/components/Stage/components/MeasurementLayer/MeasurementLayer.svelte +364 -0
- package/src/lib/components/Stage/components/MeasurementLayer/MeasurementManager.svelte +473 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/BaseMeasurement.ts +427 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/BeamMeasurement.ts +105 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/CircleMeasurement.ts +98 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/ConeMeasurement.ts +163 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/LineMeasurement.ts +102 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/RectangleMeasurement.ts +120 -0
- package/src/lib/components/Stage/components/MeasurementLayer/measurements/index.ts +7 -0
- package/src/lib/components/Stage/components/MeasurementLayer/types.ts +94 -0
- package/src/lib/components/Stage/components/MeasurementLayer/utils/canvasDrawing.ts +357 -0
- package/src/lib/components/Stage/components/MeasurementLayer/utils/distanceCalculations.ts +170 -0
- package/src/lib/components/Stage/components/ParticleSystem/ParticleSystem.svelte +220 -0
- package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/ash.png +0 -0
- package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/leaves.png +0 -0
- package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/rain.png +0 -0
- package/src/lib/components/Stage/components/ParticleSystem/particles/atlases/snow.png +0 -0
- package/src/lib/components/Stage/components/ParticleSystem/rng.js +20 -0
- package/src/lib/components/Stage/components/ParticleSystem/types.ts +95 -0
- package/src/lib/components/Stage/components/PerformanceDebugger/PerformanceDebugger.svelte +144 -0
- package/src/lib/components/Stage/components/PerformanceDebugger/index.ts +1 -0
- package/src/lib/components/Stage/components/PerformanceOverlay/PerformanceOverlay.svelte +208 -0
- package/src/lib/components/Stage/components/PerformanceOverlay/index.ts +1 -0
- package/src/lib/components/Stage/components/PointerInputManager/PointerInputManager.svelte +201 -0
- package/src/lib/components/Stage/components/Scene/Scene.svelte +651 -0
- package/src/lib/components/Stage/components/Scene/luts.ts +24 -0
- package/src/lib/components/Stage/components/Scene/types.ts +225 -0
- package/src/lib/components/Stage/components/Stage/Stage.svelte +332 -0
- package/src/lib/components/Stage/components/Stage/types.ts +136 -0
- package/src/lib/components/Stage/components/WeatherLayer/WeatherLayer.svelte +135 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/AshPreset.ts +71 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/LeavesPreset.ts +70 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/RainPreset.ts +68 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/SnowPreset.ts +70 -0
- package/src/lib/components/Stage/components/WeatherLayer/presets/index.ts +6 -0
- package/src/lib/components/Stage/components/WeatherLayer/types.ts +35 -0
- package/src/lib/components/Stage/helpers/clippingPlaneStore.svelte.ts +28 -0
- package/src/lib/components/Stage/helpers/debugState.svelte.ts +18 -0
- package/src/lib/components/Stage/helpers/grid.ts +548 -0
- package/src/lib/components/Stage/helpers/lazyBrush.ts +171 -0
- package/src/lib/components/Stage/helpers/performanceMetrics.svelte.ts +220 -0
- package/src/lib/components/Stage/helpers/utils.ts +21 -0
- package/src/lib/components/Stage/index.ts +49 -0
- package/src/lib/components/Stage/shaders/AnnotationEffects.frag +1070 -0
- package/src/lib/components/Stage/shaders/Annotations.frag +29 -0
- package/src/lib/components/Stage/shaders/Drawing.frag +83 -0
- package/src/lib/components/Stage/shaders/Drawing.vert +5 -0
- package/src/lib/components/Stage/shaders/Fog.frag +147 -0
- package/src/lib/components/Stage/shaders/FractalNoise.frag +96 -0
- package/src/lib/components/Stage/shaders/GridShader.frag +174 -0
- package/src/lib/components/Stage/shaders/Overlay.frag +23 -0
- package/src/lib/components/Stage/shaders/Overlay.vert +0 -0
- package/src/lib/components/Stage/shaders/Particles.frag +27 -0
- package/src/lib/components/Stage/shaders/Particles.vert +51 -0
- package/src/lib/components/Stage/shaders/ToolOutline.frag +59 -0
- package/src/lib/components/Stage/shaders/default.vert +8 -0
- package/src/lib/components/Stage/types.ts +4 -0
- package/src/lib/components/Table/Table.svelte +16 -0
- package/src/lib/components/Table/Td.svelte +17 -0
- package/src/lib/components/Table/Th.svelte +18 -0
- package/src/lib/components/Table/index.ts +4 -0
- package/src/lib/components/Table/types.ts +14 -0
- package/src/lib/components/Text/Text.svelte +23 -0
- package/src/lib/components/Text/index.ts +2 -0
- package/src/lib/components/Text/types.ts +12 -0
- package/src/lib/components/Title/Title.svelte +54 -0
- package/src/lib/components/Title/index.ts +2 -0
- package/src/lib/components/Title/types.ts +9 -0
- package/src/lib/components/Toast/Toast.svelte +155 -0
- package/src/lib/components/Toast/index.ts +5 -0
- package/src/lib/components/Toast/toastCookie.ts +24 -0
- package/src/lib/components/Toast/types.ts +6 -0
- package/src/lib/components/ToolTip/ToolTip.svelte +70 -0
- package/src/lib/components/ToolTip/index.ts +2 -0
- package/src/lib/components/ToolTip/types.ts +14 -0
- package/src/lib/components/index.ts +32 -0
- package/src/lib/components/types.ts +0 -0
- package/src/lib/index.ts +2 -0
- package/src/lib/styles/globals.css +108 -0
- package/src/lib/styles/normalize.css +9 -0
- package/src/lib/styles/reset.css +133 -0
- package/src/lib/styles/utilities.css +179 -0
- package/src/lib/styles/vars.css +1103 -0
- package/src/lib/types/awareness.ts +17 -0
- package/src/lib/utils/rle.ts +217 -0
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, onDestroy } from 'svelte';
|
|
3
|
+
import { Editor, type JSONContent } from '@tiptap/core';
|
|
4
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
5
|
+
import { Link } from '@tiptap/extension-link';
|
|
6
|
+
import Typography from '@tiptap/extension-typography';
|
|
7
|
+
import {
|
|
8
|
+
IconBold,
|
|
9
|
+
IconList,
|
|
10
|
+
IconListNumbers,
|
|
11
|
+
IconItalic,
|
|
12
|
+
IconLink,
|
|
13
|
+
IconSelector,
|
|
14
|
+
IconQuoteFilled,
|
|
15
|
+
IconCheck
|
|
16
|
+
} from '@tabler/icons-svelte';
|
|
17
|
+
import { Icon } from '../Icon';
|
|
18
|
+
import { Popover } from '../Popover';
|
|
19
|
+
import { Button } from '../Button';
|
|
20
|
+
import { Input } from '../Input';
|
|
21
|
+
import { computePosition, autoUpdate, offset, shift, flip } from '@floating-ui/dom';
|
|
22
|
+
import { IconButton } from '../Button';
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
height = 'auto',
|
|
26
|
+
content = $bindable(undefined),
|
|
27
|
+
debug = false,
|
|
28
|
+
editable = true,
|
|
29
|
+
onChange
|
|
30
|
+
}: {
|
|
31
|
+
height?: number | string;
|
|
32
|
+
content?: JSONContent | null | undefined;
|
|
33
|
+
debug?: boolean;
|
|
34
|
+
editable?: boolean;
|
|
35
|
+
onChange?: () => void;
|
|
36
|
+
} = $props();
|
|
37
|
+
|
|
38
|
+
let element: HTMLDivElement | undefined = $state();
|
|
39
|
+
let editor: Editor | undefined = $state();
|
|
40
|
+
let editorReady = $state(false);
|
|
41
|
+
|
|
42
|
+
// Since we're now only using JSON content, this is no longer needed
|
|
43
|
+
// const isJsonContent = content !== null && content !== undefined && typeof content === 'object';
|
|
44
|
+
|
|
45
|
+
// We'll always use JSON, but for debug purposes we'll keep track of HTML
|
|
46
|
+
let editorHtml = $state('');
|
|
47
|
+
|
|
48
|
+
// Create explicit state variables for each button's active state
|
|
49
|
+
let isBold = $state(false);
|
|
50
|
+
let isItalic = $state(false);
|
|
51
|
+
let isLink = $state(false);
|
|
52
|
+
let isParagraph = $state(false);
|
|
53
|
+
let isH1 = $state(false);
|
|
54
|
+
let isH2 = $state(false);
|
|
55
|
+
let isH3 = $state(false);
|
|
56
|
+
let isBulletList = $state(false);
|
|
57
|
+
let isOrderedList = $state(false);
|
|
58
|
+
let isBlockquote = $state(false);
|
|
59
|
+
|
|
60
|
+
// Link popover state
|
|
61
|
+
let linkPopoverVisible = $state(false);
|
|
62
|
+
let linkPopoverElement: HTMLDivElement | undefined = $state();
|
|
63
|
+
let linkInputElement: HTMLInputElement | undefined = $state();
|
|
64
|
+
let currentLinkElement: HTMLAnchorElement | null = $state(null);
|
|
65
|
+
let currentLinkUrl = $state('');
|
|
66
|
+
let cleanupAutoUpdate: (() => void) | undefined;
|
|
67
|
+
let portalContainer: HTMLDivElement | undefined = $state();
|
|
68
|
+
|
|
69
|
+
// Create portal container in body when component is mounted
|
|
70
|
+
function createPortalContainer() {
|
|
71
|
+
// Check if portal container already exists
|
|
72
|
+
const existingContainer = document.getElementById('editorLinkPortal');
|
|
73
|
+
if (existingContainer) {
|
|
74
|
+
portalContainer = existingContainer as HTMLDivElement;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Create a new portal container
|
|
79
|
+
const container = document.createElement('div');
|
|
80
|
+
container.id = 'editorLinkPortal';
|
|
81
|
+
document.body.appendChild(container);
|
|
82
|
+
portalContainer = container;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const textType = $derived.by(() => {
|
|
86
|
+
if (isH1) return 'Huge';
|
|
87
|
+
if (isH2) return 'Large';
|
|
88
|
+
if (isH3) return 'Medium';
|
|
89
|
+
if (isParagraph) return 'Normal';
|
|
90
|
+
return '';
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Function to update all active states
|
|
94
|
+
function updateActiveStates() {
|
|
95
|
+
if (!editor) return;
|
|
96
|
+
|
|
97
|
+
isBold = editor.isActive('bold');
|
|
98
|
+
isItalic = editor.isActive('italic');
|
|
99
|
+
isLink = editor.isActive('link');
|
|
100
|
+
isParagraph = editor.isActive('paragraph');
|
|
101
|
+
isH1 = editor.isActive('heading', { level: 1 });
|
|
102
|
+
isH2 = editor.isActive('heading', { level: 2 });
|
|
103
|
+
isH3 = editor.isActive('heading', { level: 3 });
|
|
104
|
+
isBlockquote = editor.isActive('blockquote');
|
|
105
|
+
isBulletList = editor.isActive('bulletList');
|
|
106
|
+
isOrderedList = editor.isActive('orderedList');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Effect to update editor content when it changes externally
|
|
110
|
+
$effect(() => {
|
|
111
|
+
if (editor && editorReady && content !== undefined) {
|
|
112
|
+
// Get current editor content
|
|
113
|
+
const currentContent = editor.getJSON();
|
|
114
|
+
|
|
115
|
+
// Only update if content actually changed (avoid infinite loops)
|
|
116
|
+
if (JSON.stringify(currentContent) !== JSON.stringify(content)) {
|
|
117
|
+
// Update editor with new content from outside
|
|
118
|
+
editor.commands.setContent(content || { type: 'doc', content: [{ type: 'paragraph' }] });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
onMount(() => {
|
|
124
|
+
// Create portal container
|
|
125
|
+
createPortalContainer();
|
|
126
|
+
|
|
127
|
+
// Default empty content
|
|
128
|
+
const emptyContent = { type: 'doc', content: [{ type: 'paragraph' }] };
|
|
129
|
+
|
|
130
|
+
// Prepare content - always ensure we have valid JSON structure
|
|
131
|
+
let initialContent;
|
|
132
|
+
|
|
133
|
+
if (content === null || content === undefined) {
|
|
134
|
+
// Use empty content if nothing provided
|
|
135
|
+
initialContent = emptyContent;
|
|
136
|
+
} else if (typeof content === 'string') {
|
|
137
|
+
// If string was passed (shouldn't happen but just in case), convert to JSON
|
|
138
|
+
try {
|
|
139
|
+
// First, try to parse it as JSON string
|
|
140
|
+
initialContent = JSON.parse(content);
|
|
141
|
+
} catch (e) {
|
|
142
|
+
console.log(e);
|
|
143
|
+
// If it's plain HTML, initialize with it
|
|
144
|
+
initialContent = content;
|
|
145
|
+
// And immediately update our bound content to be JSON
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
if (editor) {
|
|
148
|
+
content = editor.getJSON();
|
|
149
|
+
}
|
|
150
|
+
}, 0);
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
// Content is already JSON object
|
|
154
|
+
initialContent = content;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
editor = new Editor({
|
|
158
|
+
editable,
|
|
159
|
+
element: element,
|
|
160
|
+
extensions: [
|
|
161
|
+
StarterKit,
|
|
162
|
+
Typography,
|
|
163
|
+
Link.configure({
|
|
164
|
+
openOnClick: false,
|
|
165
|
+
HTMLAttributes: {
|
|
166
|
+
class: 'editor-link'
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
],
|
|
170
|
+
content: initialContent, // Pass content with fallback for null/undefined
|
|
171
|
+
onSelectionUpdate: () => {
|
|
172
|
+
updateActiveStates();
|
|
173
|
+
},
|
|
174
|
+
onUpdate: ({ editor }) => {
|
|
175
|
+
// Always update content as JSON
|
|
176
|
+
const newJson = editor.getJSON();
|
|
177
|
+
content = newJson;
|
|
178
|
+
// Update HTML for debug purposes only
|
|
179
|
+
editorHtml = editor.getHTML();
|
|
180
|
+
updateActiveStates();
|
|
181
|
+
|
|
182
|
+
// Call onChange callback if provided
|
|
183
|
+
if (onChange) {
|
|
184
|
+
onChange();
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
onTransaction: () => {
|
|
188
|
+
updateActiveStates();
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
if (element) {
|
|
193
|
+
element.addEventListener('click', handleEditorClick, true);
|
|
194
|
+
element.addEventListener('mousedown', handleEditorMouseDown, true);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
editorReady = true;
|
|
198
|
+
updateActiveStates();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
onDestroy(() => {
|
|
202
|
+
if (editor) {
|
|
203
|
+
editor.destroy();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (element) {
|
|
207
|
+
element.removeEventListener('click', handleEditorClick, true);
|
|
208
|
+
element.removeEventListener('mousedown', handleEditorMouseDown, true);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Clean up floating UI positioning if active
|
|
212
|
+
if (cleanupAutoUpdate) {
|
|
213
|
+
cleanupAutoUpdate();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Clean up the popover element from the portal container
|
|
217
|
+
if (linkPopoverElement && portalContainer && portalContainer.contains(linkPopoverElement)) {
|
|
218
|
+
portalContainer.removeChild(linkPopoverElement);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Remove portal container if it exists and we are the last editor instance
|
|
222
|
+
if (portalContainer && portalContainer.childNodes.length === 0) {
|
|
223
|
+
if (document.body.contains(portalContainer)) {
|
|
224
|
+
document.body.removeChild(portalContainer);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
function handleEditorClick(e: MouseEvent) {
|
|
230
|
+
const target = e.target as HTMLElement;
|
|
231
|
+
const linkElement = target.closest('a');
|
|
232
|
+
|
|
233
|
+
if (linkElement && linkElement.classList.contains('editor-link')) {
|
|
234
|
+
e.preventDefault();
|
|
235
|
+
e.stopPropagation();
|
|
236
|
+
e.stopImmediatePropagation();
|
|
237
|
+
|
|
238
|
+
hideLinkPopover();
|
|
239
|
+
|
|
240
|
+
currentLinkElement = linkElement;
|
|
241
|
+
currentLinkUrl = linkElement.getAttribute('href') || '';
|
|
242
|
+
showLinkPopover(linkElement);
|
|
243
|
+
return false;
|
|
244
|
+
} else if (!linkElement) {
|
|
245
|
+
hideLinkPopover();
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function handleEditorMouseDown(e: MouseEvent) {
|
|
250
|
+
const target = e.target as HTMLElement;
|
|
251
|
+
const linkElement = target.closest('a');
|
|
252
|
+
|
|
253
|
+
if (linkElement && linkElement.classList.contains('editor-link')) {
|
|
254
|
+
e.preventDefault();
|
|
255
|
+
e.stopPropagation();
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// This function is used by the <svelte:document> event handler
|
|
261
|
+
function handleDocumentClick(e: MouseEvent) {
|
|
262
|
+
// Only close if clicking outside the popover and link
|
|
263
|
+
if (linkPopoverVisible) {
|
|
264
|
+
const target = e.target as HTMLElement;
|
|
265
|
+
if (
|
|
266
|
+
linkPopoverElement &&
|
|
267
|
+
!linkPopoverElement.contains(target) &&
|
|
268
|
+
currentLinkElement !== target &&
|
|
269
|
+
!currentLinkElement?.contains(target) &&
|
|
270
|
+
!target.closest('.editor__btn') && // Don't close when clicking toolbar buttons
|
|
271
|
+
// Also check if we're clicking inside the editor but not on a link
|
|
272
|
+
!(element?.contains(target) && !target.closest('a'))
|
|
273
|
+
) {
|
|
274
|
+
hideLinkPopover();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function getSelectionCoordinates(): { left: number; bottom: number } | null {
|
|
280
|
+
if (!editor) return null;
|
|
281
|
+
|
|
282
|
+
const view = editor.view;
|
|
283
|
+
const { from, to } = view.state.selection;
|
|
284
|
+
|
|
285
|
+
if (from === to) return null; // No selection
|
|
286
|
+
|
|
287
|
+
// Get the DOM range for the current selection
|
|
288
|
+
const start = view.coordsAtPos(from);
|
|
289
|
+
const end = view.coordsAtPos(to);
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
left: (start.left + end.left) / 2,
|
|
293
|
+
bottom: Math.max(start.bottom, end.bottom)
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function showLinkPopover(anchorElement: HTMLElement) {
|
|
298
|
+
if (!linkPopoverElement || !portalContainer) return;
|
|
299
|
+
|
|
300
|
+
// Move the popover element to the portal container if it's not already there
|
|
301
|
+
if (!portalContainer.contains(linkPopoverElement)) {
|
|
302
|
+
// Create a new popover element in the portal container
|
|
303
|
+
const popoverClone = linkPopoverElement.cloneNode(true) as HTMLDivElement;
|
|
304
|
+
portalContainer.appendChild(popoverClone);
|
|
305
|
+
|
|
306
|
+
// Update our reference to the new popover element
|
|
307
|
+
linkPopoverElement = popoverClone;
|
|
308
|
+
|
|
309
|
+
// Find the input element within the cloned popover
|
|
310
|
+
linkInputElement = popoverClone.querySelector('.linkPopover__input') as HTMLInputElement;
|
|
311
|
+
|
|
312
|
+
// Setup event handlers for the portal popover
|
|
313
|
+
setupPortalPopoverEvents();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Always update the input field value with the current link URL
|
|
317
|
+
if (linkInputElement) {
|
|
318
|
+
linkInputElement.value = currentLinkUrl;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
linkPopoverVisible = true;
|
|
322
|
+
|
|
323
|
+
// Position the popover using Floating UI
|
|
324
|
+
if (cleanupAutoUpdate) {
|
|
325
|
+
cleanupAutoUpdate();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
cleanupAutoUpdate = autoUpdate(anchorElement, linkPopoverElement, () => {
|
|
329
|
+
computePosition(anchorElement, linkPopoverElement!, {
|
|
330
|
+
placement: 'bottom',
|
|
331
|
+
middleware: [offset(8), shift({ padding: 10 }), flip()],
|
|
332
|
+
strategy: 'fixed'
|
|
333
|
+
}).then(({ x, y }) => {
|
|
334
|
+
if (linkPopoverElement) {
|
|
335
|
+
Object.assign(linkPopoverElement.style, {
|
|
336
|
+
left: `${x}px`,
|
|
337
|
+
top: `${y}px`,
|
|
338
|
+
position: 'fixed',
|
|
339
|
+
display: 'block',
|
|
340
|
+
transform: 'translateZ(0)'
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Focus the input field if we're editing
|
|
347
|
+
setTimeout(() => {
|
|
348
|
+
if (linkInputElement) {
|
|
349
|
+
linkInputElement.focus();
|
|
350
|
+
linkInputElement.select();
|
|
351
|
+
}
|
|
352
|
+
}, 10);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function hideLinkPopover() {
|
|
356
|
+
linkPopoverVisible = false;
|
|
357
|
+
currentLinkElement = null;
|
|
358
|
+
currentLinkUrl = '';
|
|
359
|
+
|
|
360
|
+
if (cleanupAutoUpdate) {
|
|
361
|
+
cleanupAutoUpdate();
|
|
362
|
+
cleanupAutoUpdate = undefined;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Hide the popover in the portal if it exists
|
|
366
|
+
if (linkPopoverElement && portalContainer && portalContainer.contains(linkPopoverElement)) {
|
|
367
|
+
linkPopoverElement.style.display = 'none';
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function addOrUpdateLink() {
|
|
372
|
+
if (!editor) return;
|
|
373
|
+
|
|
374
|
+
if (currentLinkUrl.trim() === '') {
|
|
375
|
+
removeLink();
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
// If we're updating an existing link, we need to select it first
|
|
381
|
+
if (currentLinkElement && currentLinkElement.tagName === 'A') {
|
|
382
|
+
const pos = editor.view.posAtDOM(currentLinkElement, 0);
|
|
383
|
+
const end = pos + currentLinkElement.textContent!.length;
|
|
384
|
+
editor.chain().focus().setTextSelection({ from: pos, to: end }).run();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
editor.chain().focus().extendMarkRange('link').setLink({ href: currentLinkUrl }).run();
|
|
388
|
+
hideLinkPopover();
|
|
389
|
+
} catch (e) {
|
|
390
|
+
console.error(e);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function removeLink() {
|
|
395
|
+
if (!editor) return;
|
|
396
|
+
|
|
397
|
+
if (currentLinkElement && currentLinkElement.tagName === 'A') {
|
|
398
|
+
const pos = editor.view.posAtDOM(currentLinkElement, 0);
|
|
399
|
+
const end = pos + currentLinkElement.textContent!.length;
|
|
400
|
+
editor.chain().focus().setTextSelection({ from: pos, to: end }).unsetLink().run();
|
|
401
|
+
} else {
|
|
402
|
+
editor.chain().focus().extendMarkRange('link').unsetLink().run();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
hideLinkPopover();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function visitLink() {
|
|
409
|
+
if (currentLinkUrl) {
|
|
410
|
+
window.open(currentLinkUrl, '_blank');
|
|
411
|
+
hideLinkPopover();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Setup portal popover event handlers when the component is mounted
|
|
416
|
+
function setupPortalPopoverEvents() {
|
|
417
|
+
if (!linkPopoverElement || !portalContainer) return;
|
|
418
|
+
|
|
419
|
+
// Add event handlers for the buttons in the portal popover
|
|
420
|
+
const confirmButton = linkPopoverElement.querySelector('.linkPopover__inputRow button') as HTMLElement;
|
|
421
|
+
if (confirmButton) {
|
|
422
|
+
// Use the correct typing approach for TypeScript with Svelte 5
|
|
423
|
+
(confirmButton as unknown as { onclick: typeof addOrUpdateLink }).onclick = addOrUpdateLink;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Add event handlers for the visit and remove buttons
|
|
427
|
+
const buttons = linkPopoverElement.querySelectorAll('.linkPopover__actions button');
|
|
428
|
+
if (buttons.length >= 2) {
|
|
429
|
+
(buttons[0] as unknown as { onclick: typeof visitLink }).onclick = visitLink;
|
|
430
|
+
(buttons[1] as unknown as { onclick: typeof removeLink }).onclick = removeLink;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (linkInputElement) {
|
|
434
|
+
// Set up keydown handler for Enter key
|
|
435
|
+
(linkInputElement as unknown as { onkeydown: typeof handleLinkInputKeydown }).onkeydown = handleLinkInputKeydown;
|
|
436
|
+
|
|
437
|
+
// Set up input handler to keep our state variable in sync
|
|
438
|
+
linkInputElement.addEventListener('input', (e: Event) => {
|
|
439
|
+
currentLinkUrl = (e.target as HTMLInputElement).value;
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function setNewLink() {
|
|
445
|
+
if (!editor) return;
|
|
446
|
+
|
|
447
|
+
// For creating a new link (toolbar button)
|
|
448
|
+
hideLinkPopover(); // Close any existing popover first
|
|
449
|
+
|
|
450
|
+
const selection = editor.view.state.selection;
|
|
451
|
+
const hasSelection = !selection.empty;
|
|
452
|
+
|
|
453
|
+
if (hasSelection) {
|
|
454
|
+
// With selection, first create the link with a temporary URL
|
|
455
|
+
currentLinkUrl = '';
|
|
456
|
+
editor.chain().focus().extendMarkRange('link').setLink({ href: 'https://' }).run();
|
|
457
|
+
|
|
458
|
+
// Now find the newly created link
|
|
459
|
+
setTimeout(() => {
|
|
460
|
+
// Get coordinates of the selection
|
|
461
|
+
const coords = getSelectionCoordinates();
|
|
462
|
+
if (!coords) return;
|
|
463
|
+
|
|
464
|
+
// Create a temp anchor element for positioning the popover
|
|
465
|
+
const tempElement = document.createElement('span');
|
|
466
|
+
tempElement.style.position = 'absolute';
|
|
467
|
+
tempElement.style.left = `${coords.left}px`;
|
|
468
|
+
tempElement.style.top = `${coords.bottom}px`;
|
|
469
|
+
document.body.appendChild(tempElement);
|
|
470
|
+
|
|
471
|
+
currentLinkElement = tempElement as unknown as HTMLAnchorElement;
|
|
472
|
+
showLinkPopover(tempElement);
|
|
473
|
+
|
|
474
|
+
// Clean up the temporary element after we're done
|
|
475
|
+
setTimeout(() => {
|
|
476
|
+
if (document.body.contains(tempElement)) {
|
|
477
|
+
document.body.removeChild(tempElement);
|
|
478
|
+
}
|
|
479
|
+
}, 100);
|
|
480
|
+
}, 10);
|
|
481
|
+
} else {
|
|
482
|
+
// For empty selection, insert a placeholder link
|
|
483
|
+
currentLinkUrl = 'https://';
|
|
484
|
+
|
|
485
|
+
// Insert link with default text
|
|
486
|
+
editor.chain().focus().insertContent('<a href="https://">link</a>').run();
|
|
487
|
+
|
|
488
|
+
// Find the newly inserted link and show popover
|
|
489
|
+
setTimeout(() => {
|
|
490
|
+
if (!element) return;
|
|
491
|
+
|
|
492
|
+
const links = element.querySelectorAll('a[href="https://"]');
|
|
493
|
+
if (links && links.length > 0) {
|
|
494
|
+
currentLinkElement = links[links.length - 1] as HTMLAnchorElement;
|
|
495
|
+
showLinkPopover(currentLinkElement);
|
|
496
|
+
}
|
|
497
|
+
}, 10);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Handle enter key in the link input
|
|
502
|
+
function handleLinkInputKeydown(e: KeyboardEvent) {
|
|
503
|
+
if (e.key === 'Enter') {
|
|
504
|
+
e.preventDefault();
|
|
505
|
+
addOrUpdateLink();
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
</script>
|
|
509
|
+
|
|
510
|
+
<svelte:document on:click={handleDocumentClick} />
|
|
511
|
+
|
|
512
|
+
<div class={['editor', !editable && 'editor--notEdtiable']} style={`height: ${height}; max-height: ${height}`}>
|
|
513
|
+
{#if editable}
|
|
514
|
+
<div class="editor__toolbar">
|
|
515
|
+
{#if editor}
|
|
516
|
+
<button
|
|
517
|
+
onclick={() => editor?.chain().focus().toggleBold().run()}
|
|
518
|
+
class={['editor__btn', isBold && 'isActive']}
|
|
519
|
+
>
|
|
520
|
+
<Icon Icon={IconBold} size="20px" stroke={2} />
|
|
521
|
+
</button>
|
|
522
|
+
<button
|
|
523
|
+
onclick={() => editor?.chain().focus().toggleItalic().run()}
|
|
524
|
+
class={['editor__btn', isItalic && 'isActive']}
|
|
525
|
+
>
|
|
526
|
+
<Icon Icon={IconItalic} size="20px" stroke={2} />
|
|
527
|
+
</button>
|
|
528
|
+
<button onclick={setNewLink} class={['editor__btn', isLink && 'isActive']}>
|
|
529
|
+
<Icon Icon={IconLink} size="20px" stroke={2} />
|
|
530
|
+
</button>
|
|
531
|
+
<button
|
|
532
|
+
onclick={() => editor?.chain().focus().toggleBulletList().run()}
|
|
533
|
+
class={['editor__btn', isBulletList && 'isActive']}
|
|
534
|
+
>
|
|
535
|
+
<Icon Icon={IconList} size="20px" stroke={2} />
|
|
536
|
+
</button>
|
|
537
|
+
<button
|
|
538
|
+
onclick={() => editor?.chain().focus().toggleOrderedList().run()}
|
|
539
|
+
class={['editor__btn', isOrderedList && 'isActive']}
|
|
540
|
+
>
|
|
541
|
+
<Icon Icon={IconListNumbers} size="20px" stroke={2} />
|
|
542
|
+
</button>
|
|
543
|
+
<button
|
|
544
|
+
onclick={() => editor?.chain().focus().toggleBlockquote().run()}
|
|
545
|
+
class={['editor__btn', isBlockquote && 'isActive']}
|
|
546
|
+
>
|
|
547
|
+
<Icon Icon={IconQuoteFilled} size="20px" stroke={2} />
|
|
548
|
+
</button>
|
|
549
|
+
<Popover>
|
|
550
|
+
{#snippet trigger()}
|
|
551
|
+
<div class="editor__toolbarTrigger">
|
|
552
|
+
{textType}
|
|
553
|
+
<Icon Icon={IconSelector} size="1rem" color="var(--fgMuted)" />
|
|
554
|
+
</div>
|
|
555
|
+
{/snippet}
|
|
556
|
+
{#snippet content()}
|
|
557
|
+
<div class="editor__toolbarTextOptions">
|
|
558
|
+
<button
|
|
559
|
+
onclick={() => editor?.chain().focus().setParagraph().run()}
|
|
560
|
+
class:active={['editor__toolbarTextP', isParagraph && 'isActive']}
|
|
561
|
+
>
|
|
562
|
+
Normal
|
|
563
|
+
</button>
|
|
564
|
+
<button
|
|
565
|
+
onclick={() => editor?.chain().focus().toggleHeading({ level: 3 }).run()}
|
|
566
|
+
class={['editor__toolbarTextH3', isH3 && 'isActive']}
|
|
567
|
+
>
|
|
568
|
+
Medium
|
|
569
|
+
</button>
|
|
570
|
+
<button
|
|
571
|
+
onclick={() => editor?.chain().focus().toggleHeading({ level: 2 }).run()}
|
|
572
|
+
class={['editor__toolbarTextH2', isH2 && 'isActive']}
|
|
573
|
+
>
|
|
574
|
+
Large
|
|
575
|
+
</button>
|
|
576
|
+
<button
|
|
577
|
+
onclick={() => editor?.chain().focus().toggleHeading({ level: 1 }).run()}
|
|
578
|
+
class={['editor__toolbarTextH1', isH1 && 'isActive']}
|
|
579
|
+
>
|
|
580
|
+
Huge
|
|
581
|
+
</button>
|
|
582
|
+
</div>
|
|
583
|
+
{/snippet}
|
|
584
|
+
</Popover>
|
|
585
|
+
{/if}
|
|
586
|
+
</div>
|
|
587
|
+
{/if}
|
|
588
|
+
|
|
589
|
+
<div class="editor__content">
|
|
590
|
+
<div bind:this={element}></div>
|
|
591
|
+
</div>
|
|
592
|
+
|
|
593
|
+
<!-- Link Popover Template (hidden) - Will be cloned to portal container -->
|
|
594
|
+
<div bind:this={linkPopoverElement} class="linkPopover" style="display: none;">
|
|
595
|
+
<div class="linkPopover__content">
|
|
596
|
+
<div class="linkPopover__inputRow">
|
|
597
|
+
<Input
|
|
598
|
+
bind:element={linkInputElement}
|
|
599
|
+
type="text"
|
|
600
|
+
bind:value={currentLinkUrl}
|
|
601
|
+
placeholder="https://"
|
|
602
|
+
class="linkPopover__input"
|
|
603
|
+
onkeydown={handleLinkInputKeydown}
|
|
604
|
+
/>
|
|
605
|
+
<IconButton onclick={addOrUpdateLink}>
|
|
606
|
+
<Icon Icon={IconCheck} stroke={2} />
|
|
607
|
+
</IconButton>
|
|
608
|
+
</div>
|
|
609
|
+
<div class="linkPopover__actions">
|
|
610
|
+
<Button onclick={visitLink}>Go to link</Button>
|
|
611
|
+
<Button variant="danger" onclick={removeLink}>Remove</Button>
|
|
612
|
+
</div>
|
|
613
|
+
</div>
|
|
614
|
+
</div>
|
|
615
|
+
|
|
616
|
+
{#if debug}
|
|
617
|
+
<div class="editor__state">
|
|
618
|
+
<h4>Editor Status: {editorReady ? 'Ready' : 'Loading...'}</h4>
|
|
619
|
+
<h4>Content Type: JSON</h4>
|
|
620
|
+
{#if content === null || content === undefined}
|
|
621
|
+
<pre>null/undefined</pre>
|
|
622
|
+
{:else}
|
|
623
|
+
<pre>{JSON.stringify(content, null, 2)}</pre>
|
|
624
|
+
{/if}
|
|
625
|
+
|
|
626
|
+
<h4>HTML representation:</h4>
|
|
627
|
+
<pre>{editorHtml}</pre>
|
|
628
|
+
</div>
|
|
629
|
+
{/if}
|
|
630
|
+
</div>
|
|
631
|
+
|
|
632
|
+
<style>
|
|
633
|
+
.editor {
|
|
634
|
+
position: relative;
|
|
635
|
+
width: 100%;
|
|
636
|
+
border: var(--borderThin);
|
|
637
|
+
border-radius: var(--radius-2);
|
|
638
|
+
overflow-y: auto;
|
|
639
|
+
}
|
|
640
|
+
.editor.editor--notEdtiable {
|
|
641
|
+
pointer-events: none;
|
|
642
|
+
border: none;
|
|
643
|
+
border-radius: 0;
|
|
644
|
+
}
|
|
645
|
+
.editor.editor--notEdtiable .editor__content {
|
|
646
|
+
pointer-events: none;
|
|
647
|
+
padding: 0;
|
|
648
|
+
}
|
|
649
|
+
.editor__toolbar {
|
|
650
|
+
position: sticky;
|
|
651
|
+
top: 0;
|
|
652
|
+
background-color: var(--bg);
|
|
653
|
+
z-index: 2;
|
|
654
|
+
display: flex;
|
|
655
|
+
gap: 0.25rem;
|
|
656
|
+
padding: 0.5rem 0.5rem;
|
|
657
|
+
}
|
|
658
|
+
.editor__content {
|
|
659
|
+
padding: 1rem;
|
|
660
|
+
position: relative;
|
|
661
|
+
}
|
|
662
|
+
.editor__state {
|
|
663
|
+
padding: 1rem;
|
|
664
|
+
border-top: var(--borderThin);
|
|
665
|
+
background-color: var(--contrastLow);
|
|
666
|
+
font-size: 0.85rem;
|
|
667
|
+
}
|
|
668
|
+
.editor__state pre {
|
|
669
|
+
max-height: 150px;
|
|
670
|
+
overflow-y: auto;
|
|
671
|
+
background-color: var(--contrastMedium);
|
|
672
|
+
padding: 0.5rem;
|
|
673
|
+
border-radius: var(--radius-2);
|
|
674
|
+
}
|
|
675
|
+
.editor__btn {
|
|
676
|
+
border: solid 2px transparent;
|
|
677
|
+
border-radius: var(--radius-2);
|
|
678
|
+
display: flex;
|
|
679
|
+
align-items: center;
|
|
680
|
+
justify-content: center;
|
|
681
|
+
cursor: pointer;
|
|
682
|
+
height: 2rem;
|
|
683
|
+
width: 2rem;
|
|
684
|
+
}
|
|
685
|
+
.editor__btn.isActive {
|
|
686
|
+
border-color: var(--fg);
|
|
687
|
+
}
|
|
688
|
+
.editor__btn:hover {
|
|
689
|
+
background-color: var(--iconBtn-bgHover);
|
|
690
|
+
border: var(--iconBtn-borderHover);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.linkPopover {
|
|
694
|
+
position: fixed;
|
|
695
|
+
z-index: 9999;
|
|
696
|
+
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1));
|
|
697
|
+
will-change: transform;
|
|
698
|
+
}
|
|
699
|
+
.linkPopover__content {
|
|
700
|
+
box-shadow: var(--shadow-1);
|
|
701
|
+
background-color: var(--bg);
|
|
702
|
+
border: var(--borderThin);
|
|
703
|
+
border-radius: var(--radius-2);
|
|
704
|
+
padding: 0.5rem;
|
|
705
|
+
width: 280px;
|
|
706
|
+
}
|
|
707
|
+
.linkPopover__inputRow {
|
|
708
|
+
display: flex;
|
|
709
|
+
gap: 0.5rem;
|
|
710
|
+
margin-bottom: 0.75rem;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
:global(#editorLinkPortal) {
|
|
714
|
+
position: fixed;
|
|
715
|
+
top: 0;
|
|
716
|
+
left: 0;
|
|
717
|
+
width: 0;
|
|
718
|
+
height: 0;
|
|
719
|
+
z-index: 9999;
|
|
720
|
+
pointer-events: none;
|
|
721
|
+
}
|
|
722
|
+
:global(#editorLinkPortal .linkPopover) {
|
|
723
|
+
pointer-events: auto;
|
|
724
|
+
}
|
|
725
|
+
:global {
|
|
726
|
+
.tiptap:focus-visible {
|
|
727
|
+
outline: none;
|
|
728
|
+
}
|
|
729
|
+
.editor__content p {
|
|
730
|
+
line-height: 1.5;
|
|
731
|
+
}
|
|
732
|
+
.editor__content ul {
|
|
733
|
+
list-style-type: disc;
|
|
734
|
+
margin-left: 1.5rem;
|
|
735
|
+
}
|
|
736
|
+
.editor__content ol {
|
|
737
|
+
list-style-type: decimal;
|
|
738
|
+
margin-left: 1.5rem;
|
|
739
|
+
}
|
|
740
|
+
.editor__content h1,
|
|
741
|
+
.editor__content h2,
|
|
742
|
+
.editor__content h3 {
|
|
743
|
+
font-weight: 600;
|
|
744
|
+
}
|
|
745
|
+
.editor__content h1 {
|
|
746
|
+
font-size: 2rem;
|
|
747
|
+
}
|
|
748
|
+
.editor__content h2 {
|
|
749
|
+
font-size: 1.5rem;
|
|
750
|
+
}
|
|
751
|
+
.editor__content h3 {
|
|
752
|
+
font-size: 1.25rem;
|
|
753
|
+
}
|
|
754
|
+
.editor__content a {
|
|
755
|
+
color: var(--link-color);
|
|
756
|
+
text-decoration: none;
|
|
757
|
+
cursor: pointer;
|
|
758
|
+
font-weight: var(--font-weight-6);
|
|
759
|
+
}
|
|
760
|
+
.editor__content a:hover {
|
|
761
|
+
text-decoration: underline;
|
|
762
|
+
}
|
|
763
|
+
.editor__content strong {
|
|
764
|
+
font-weight: 700;
|
|
765
|
+
}
|
|
766
|
+
.editor__content em {
|
|
767
|
+
font-style: italic;
|
|
768
|
+
}
|
|
769
|
+
.editor__content blockquote {
|
|
770
|
+
border-left: 4px solid var(--contrastMedium);
|
|
771
|
+
color: var(--fgMuted);
|
|
772
|
+
padding-left: 1rem;
|
|
773
|
+
margin-left: 0;
|
|
774
|
+
font-family: var(--font-sans);
|
|
775
|
+
}
|
|
776
|
+
.editor__content hr {
|
|
777
|
+
height: 1px;
|
|
778
|
+
background-color: var(--contrastMedium);
|
|
779
|
+
}
|
|
780
|
+
.editor__toolbarTextOptions {
|
|
781
|
+
display: flex;
|
|
782
|
+
flex-direction: column;
|
|
783
|
+
gap: 0.5rem;
|
|
784
|
+
}
|
|
785
|
+
.editor__toolbarTextOptions button {
|
|
786
|
+
cursor: pointer;
|
|
787
|
+
padding: 0.25rem 0.5rem;
|
|
788
|
+
width: 100%;
|
|
789
|
+
}
|
|
790
|
+
.editor__toolbarTextOptions button:hover {
|
|
791
|
+
background-color: var(--contrastLow);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
.editor__toolbarTextH1,
|
|
795
|
+
.editor__toolbarTextH2,
|
|
796
|
+
.editor__toolbarTextH3 {
|
|
797
|
+
font-weight: 600;
|
|
798
|
+
}
|
|
799
|
+
.editor__toolbarTextP {
|
|
800
|
+
font-weight: normal;
|
|
801
|
+
}
|
|
802
|
+
.editor__toolbarTextH1 {
|
|
803
|
+
font-size: 2.5rem;
|
|
804
|
+
}
|
|
805
|
+
.editor__toolbarTextH2 {
|
|
806
|
+
font-size: 2rem;
|
|
807
|
+
}
|
|
808
|
+
.editor__toolbarTextH3 {
|
|
809
|
+
font-size: 1.5rem;
|
|
810
|
+
}
|
|
811
|
+
.editor__toolbarTrigger {
|
|
812
|
+
display: flex;
|
|
813
|
+
align-items: center;
|
|
814
|
+
gap: 0.5rem;
|
|
815
|
+
cursor: pointer;
|
|
816
|
+
border: solid 2px transparent;
|
|
817
|
+
padding: 0.25rem 0.5rem;
|
|
818
|
+
border-radius: var(--radius-2);
|
|
819
|
+
}
|
|
820
|
+
.editor__toolbarTrigger:hover {
|
|
821
|
+
background-color: var(--iconBtn-bgHover);
|
|
822
|
+
border: var(--iconBtn-borderHover);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
</style>
|