@bendyline/squisq-editor-react 1.6.0 → 1.6.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/README.md +57 -10
- package/dist/index.d.ts +401 -76
- package/dist/index.js +1334 -930
- package/dist/index.js.map +1 -1
- package/dist/styles/fa-brands-400-AHOAZHCU.woff2 +0 -0
- package/dist/styles/fa-regular-400-VRZYIBIZ.woff2 +0 -0
- package/dist/styles/fa-solid-900-MDEYK55F.woff2 +0 -0
- package/dist/styles/fa-v4compatibility-ETEVP6IB.woff2 +0 -0
- package/dist/styles/index.css +13867 -0
- package/package.json +15 -7
- package/src/EditorContext.tsx +22 -16
- package/src/EditorShell.tsx +68 -27
- package/src/OutlinePanel.tsx +26 -4
- package/src/PreviewControls.tsx +338 -141
- package/src/PreviewPanel.tsx +15 -9
- package/src/RawEditor.tsx +10 -4
- package/src/Toolbar.tsx +21 -11
- package/src/VersionHistoryPanel.tsx +2 -2
- package/src/__tests__/codeContextSectionView.test.tsx +95 -0
- package/src/__tests__/codeContextZoneManager.test.ts +127 -0
- package/src/__tests__/diffContextSections.test.ts +39 -0
- package/src/__tests__/editorShellCodeContext.test.tsx +86 -0
- package/src/__tests__/editorShellProps.test.tsx +96 -0
- package/src/__tests__/previewControls.test.tsx +70 -0
- package/src/__tests__/useJsonEditorTokens.test.ts +59 -0
- package/src/__tests__/useMediaRecorder.test.ts +17 -0
- package/src/codeContext/CodeContextSectionView.tsx +124 -0
- package/src/codeContext/CodeContextZoneManager.ts +149 -0
- package/src/codeContext/CodeContextZones.tsx +121 -0
- package/src/codeContext/diffContextSections.ts +38 -0
- package/src/codeContext/types.ts +75 -0
- package/src/index.ts +32 -1
- package/src/jsonEditor/useJsonEditorTokens.ts +13 -43
- package/src/recorder/hooks/useMediaRecorder.ts +9 -10
- package/src/styles/code-context.css +155 -0
- package/src/styles/editor.css +149 -3
- package/src/styles/index.css +1 -0
package/src/index.ts
CHANGED
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
|
|
20
20
|
// Shell (top-level component)
|
|
21
21
|
export { EditorShell } from './EditorShell.js';
|
|
22
|
-
export type { EditorShellProps,
|
|
22
|
+
export type { EditorShellProps, EditorColorScheme } from './EditorShell.js';
|
|
23
|
+
|
|
24
|
+
// Code-context sections (host-supplied markdown injected into the code surface)
|
|
25
|
+
export { CodeContextZones } from './codeContext/CodeContextZones.js';
|
|
26
|
+
export type { CodeContext, CodeContextSection } from './codeContext/types.js';
|
|
23
27
|
|
|
24
28
|
// FolderView — standalone folder browser surface (companion to the shell)
|
|
25
29
|
export { FolderView } from './FolderView.js';
|
|
@@ -100,6 +104,8 @@ export type { EmojiEntry, EmojiCategory } from './emojiData.js';
|
|
|
100
104
|
export {
|
|
101
105
|
PreviewSettingsProvider,
|
|
102
106
|
PreviewToolbarControls,
|
|
107
|
+
PreviewModeSwitch,
|
|
108
|
+
PreviewFormatSwitch,
|
|
103
109
|
usePreviewSettings,
|
|
104
110
|
} from './PreviewControls.js';
|
|
105
111
|
export type { PreviewSettings } from './PreviewControls.js';
|
|
@@ -178,6 +184,31 @@ export {
|
|
|
178
184
|
processTextFiles,
|
|
179
185
|
} from './utils/dropUtils.js';
|
|
180
186
|
|
|
187
|
+
// Monaco lazy loader — exported so hosts embedding RawEditor-like surfaces
|
|
188
|
+
// can share the same load-once Monaco bootstrap.
|
|
189
|
+
export { useMonacoLoader } from './useMonacoLoader.js';
|
|
190
|
+
export type { UseMonacoLoaderResult } from './useMonacoLoader.js';
|
|
191
|
+
|
|
192
|
+
// Custom themes — provider stack (doc frontmatter + browser-local library)
|
|
193
|
+
export { CustomThemeProvider, useCustomThemes, useDocCustomThemes } from './customThemes/index.js';
|
|
194
|
+
export type {
|
|
195
|
+
CustomThemeContextValue,
|
|
196
|
+
CustomThemeProviderProps,
|
|
197
|
+
DocCustomThemes,
|
|
198
|
+
} from './customThemes/index.js';
|
|
199
|
+
|
|
200
|
+
// Custom templates — provider stack (doc frontmatter + browser-local library)
|
|
201
|
+
export {
|
|
202
|
+
CustomTemplateProvider,
|
|
203
|
+
useCustomTemplates,
|
|
204
|
+
useDocCustomTemplates,
|
|
205
|
+
} from './customTemplates/index.js';
|
|
206
|
+
export type {
|
|
207
|
+
CustomTemplateContextValue,
|
|
208
|
+
CustomTemplateProviderProps,
|
|
209
|
+
DocCustomTemplates,
|
|
210
|
+
} from './customTemplates/index.js';
|
|
211
|
+
|
|
181
212
|
// Bridge utilities
|
|
182
213
|
export { markdownToTiptap, tiptapToMarkdown } from './tiptapBridge.js';
|
|
183
214
|
|
|
@@ -6,15 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
import { useMemo } from 'react';
|
|
8
8
|
import type { CSSProperties } from 'react';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
type SurfaceScheme,
|
|
13
|
-
type Theme,
|
|
14
|
-
DEFAULT_THEME,
|
|
15
|
-
DARK_SURFACE,
|
|
16
|
-
LIGHT_SURFACE,
|
|
17
|
-
} from '@bendyline/squisq/schemas';
|
|
9
|
+
import { type SurfaceScheme, type Theme } from '@bendyline/squisq/schemas';
|
|
10
|
+
import { buildJsonFormTokens, resolveJsonFormTheme } from '@bendyline/squisq/jsonForm';
|
|
11
|
+
import { useAutoSurface } from '@bendyline/squisq-react';
|
|
18
12
|
|
|
19
13
|
export interface JsonEditorTokens {
|
|
20
14
|
style: CSSProperties;
|
|
@@ -25,39 +19,15 @@ export function useJsonEditorTokens(
|
|
|
25
19
|
theme: Theme | undefined,
|
|
26
20
|
surface: SurfaceScheme | 'auto' | undefined,
|
|
27
21
|
): JsonEditorTokens {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
? typeof window !== 'undefined' &&
|
|
33
|
-
window.matchMedia?.('(prefers-color-scheme: dark)').matches
|
|
34
|
-
? DARK_SURFACE
|
|
35
|
-
: LIGHT_SURFACE
|
|
36
|
-
: (surface ?? undefined);
|
|
37
|
-
const finalTheme = resolvedSurface ? applySurface(baseTheme, resolvedSurface) : baseTheme;
|
|
38
|
-
|
|
39
|
-
const titleFont = resolveFontFamily(finalTheme.typography.titleFont, 'system-ui, sans-serif');
|
|
40
|
-
const bodyFont = resolveFontFamily(finalTheme.typography.bodyFont, 'system-ui, sans-serif');
|
|
41
|
-
const monoFont = resolveFontFamily(
|
|
42
|
-
finalTheme.typography.monoFont,
|
|
43
|
-
'ui-monospace, Consolas, monospace',
|
|
44
|
-
);
|
|
22
|
+
// Reactive `prefers-color-scheme` tracking (matches `<JsonView>`), so the
|
|
23
|
+
// editor re-themes live when the OS switches light/dark under `'auto'`.
|
|
24
|
+
const auto = useAutoSurface(surface === 'auto');
|
|
25
|
+
const effectiveSurface = surface === 'auto' ? auto : (surface ?? undefined);
|
|
45
26
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
['--squisq-jsonform-warning' as string]: finalTheme.colors.warning,
|
|
53
|
-
['--squisq-jsonform-border' as string]: `color-mix(in srgb, ${finalTheme.colors.textMuted} 35%, transparent)`,
|
|
54
|
-
['--squisq-jsonform-input-bg' as string]: finalTheme.colors.backgroundLight,
|
|
55
|
-
['--squisq-jsonform-title-font' as string]: titleFont,
|
|
56
|
-
['--squisq-jsonform-body-font' as string]: bodyFont,
|
|
57
|
-
['--squisq-jsonform-mono-font' as string]: monoFont,
|
|
58
|
-
['--squisq-jsonform-radius' as string]: `${finalTheme.style.borderRadius ?? 8}px`,
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
return { style, theme: finalTheme };
|
|
62
|
-
}, [theme, surface]);
|
|
27
|
+
return useMemo(() => {
|
|
28
|
+
const style = buildJsonFormTokens(theme, effectiveSurface, {
|
|
29
|
+
prefix: '--squisq-jsonform',
|
|
30
|
+
}) as unknown as CSSProperties;
|
|
31
|
+
return { style, theme: resolveJsonFormTheme(theme, effectiveSurface) };
|
|
32
|
+
}, [theme, effectiveSurface]);
|
|
63
33
|
}
|
|
@@ -35,8 +35,8 @@ export type RecorderState =
|
|
|
35
35
|
| 'error';
|
|
36
36
|
|
|
37
37
|
export interface UseMediaRecorderOptions {
|
|
38
|
-
/** Which capture pipeline to use. */
|
|
39
|
-
source
|
|
38
|
+
/** Which capture pipeline to use (default: `'mic'`). */
|
|
39
|
+
source?: RecorderSource;
|
|
40
40
|
/**
|
|
41
41
|
* Preferred MIME type override. When the browser supports it, this
|
|
42
42
|
* wins over the default candidate list. When unset (or unsupported),
|
|
@@ -115,9 +115,10 @@ export interface UseMediaRecorderResult {
|
|
|
115
115
|
* resources (e.g. the screen+mic AudioContext mixer).
|
|
116
116
|
*/
|
|
117
117
|
async function acquireStream(
|
|
118
|
+
source: RecorderSource,
|
|
118
119
|
opts: UseMediaRecorderOptions,
|
|
119
120
|
): Promise<{ stream: MediaStream; dispose: () => void }> {
|
|
120
|
-
switch (
|
|
121
|
+
switch (source) {
|
|
121
122
|
case 'mic': {
|
|
122
123
|
const audio = typeof opts.audioConstraints === 'object' ? opts.audioConstraints : undefined;
|
|
123
124
|
const stream = await requestMicStream(audio);
|
|
@@ -135,7 +136,7 @@ async function acquireStream(
|
|
|
135
136
|
const handle: ScreenStreamHandle = await requestScreenStream({
|
|
136
137
|
video: opts.videoConstraints ?? true,
|
|
137
138
|
systemAudio: opts.systemAudio ?? false,
|
|
138
|
-
includeMicrophone:
|
|
139
|
+
includeMicrophone: source === 'screen+mic',
|
|
139
140
|
microphoneConstraints:
|
|
140
141
|
typeof opts.audioConstraints === 'object' ? opts.audioConstraints : undefined,
|
|
141
142
|
});
|
|
@@ -158,7 +159,7 @@ export function getCaptureKind(source: RecorderSource): CaptureKind {
|
|
|
158
159
|
return captureKindFor(source);
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
export function useMediaRecorder(options: UseMediaRecorderOptions): UseMediaRecorderResult {
|
|
162
|
+
export function useMediaRecorder(options: UseMediaRecorderOptions = {}): UseMediaRecorderResult {
|
|
162
163
|
const [state, setState] = useState<RecorderState>('idle');
|
|
163
164
|
const [stream, setStream] = useState<MediaStream | null>(null);
|
|
164
165
|
const [blob, setBlob] = useState<Blob | null>(null);
|
|
@@ -256,11 +257,9 @@ export function useMediaRecorder(options: UseMediaRecorderOptions): UseMediaReco
|
|
|
256
257
|
setError(null);
|
|
257
258
|
setState('requesting');
|
|
258
259
|
try {
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
optionsRef.current.mimeType,
|
|
263
|
-
);
|
|
260
|
+
const source = optionsRef.current.source ?? 'mic';
|
|
261
|
+
const { stream: nextStream, dispose } = await acquireStream(source, optionsRef.current);
|
|
262
|
+
const resolved = resolveFormat(captureKindFor(source), optionsRef.current.mimeType);
|
|
264
263
|
const recorderOptions: MediaRecorderOptions = {};
|
|
265
264
|
if (resolved.mimeType) recorderOptions.mimeType = resolved.mimeType;
|
|
266
265
|
if (optionsRef.current.bitsPerSecond) {
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-context sections — host-supplied markdown blurbs rendered inside
|
|
3
|
+
* Monaco view zones above anchor lines (see src/codeContext/).
|
|
4
|
+
*
|
|
5
|
+
* The zone dom node lives in Monaco's `.view-zones` layer, which sits below
|
|
6
|
+
* `.view-lines` in hit-test order. Raising the zone's z-index inside that
|
|
7
|
+
* shared stacking context is what makes section content clickable; Monaco's
|
|
8
|
+
* own `suppressMouseDown` keeps the cursor from jumping.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
.squisq-ccx-zone {
|
|
12
|
+
z-index: 10;
|
|
13
|
+
pointer-events: auto;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.squisq-ccx-section {
|
|
18
|
+
--ccx-fg: #57606a;
|
|
19
|
+
--ccx-fg-strong: #24292f;
|
|
20
|
+
--ccx-bg: rgba(175, 184, 193, 0.12);
|
|
21
|
+
--ccx-bg-hover: rgba(175, 184, 193, 0.22);
|
|
22
|
+
--ccx-border: rgba(140, 149, 159, 0.35);
|
|
23
|
+
--ccx-accent: #0969da;
|
|
24
|
+
font-family: var(--squisq-ux-font, system-ui, -apple-system, sans-serif);
|
|
25
|
+
font-size: 12px;
|
|
26
|
+
line-height: 1.45;
|
|
27
|
+
color: var(--ccx-fg);
|
|
28
|
+
border-left: 2px solid var(--ccx-border);
|
|
29
|
+
background: var(--ccx-bg);
|
|
30
|
+
margin: 2px 8px 2px 4px;
|
|
31
|
+
border-radius: 4px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-ccx-section {
|
|
35
|
+
--ccx-fg: #9198a1;
|
|
36
|
+
--ccx-fg-strong: #e6edf3;
|
|
37
|
+
--ccx-bg: rgba(110, 118, 129, 0.14);
|
|
38
|
+
--ccx-bg-hover: rgba(110, 118, 129, 0.26);
|
|
39
|
+
--ccx-border: rgba(110, 118, 129, 0.45);
|
|
40
|
+
--ccx-accent: #4493f8;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* ── Collapsed strip ─────────────────────────────────────────────── */
|
|
44
|
+
|
|
45
|
+
.squisq-ccx-strip {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 6px;
|
|
49
|
+
width: 100%;
|
|
50
|
+
padding: 3px 10px;
|
|
51
|
+
border: none;
|
|
52
|
+
background: transparent;
|
|
53
|
+
color: inherit;
|
|
54
|
+
font: inherit;
|
|
55
|
+
text-align: left;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
border-radius: 4px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.squisq-ccx-strip:hover {
|
|
61
|
+
background: var(--ccx-bg-hover);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.squisq-ccx-chevron {
|
|
65
|
+
flex: 0 0 auto;
|
|
66
|
+
font-size: 9px;
|
|
67
|
+
opacity: 0.7;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.squisq-ccx-strip-text {
|
|
71
|
+
flex: 1 1 auto;
|
|
72
|
+
min-width: 0;
|
|
73
|
+
white-space: nowrap;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
text-overflow: ellipsis;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Flatten the strip's markdown to a single inline line. */
|
|
79
|
+
.squisq-ccx-strip-text .squisq-md,
|
|
80
|
+
.squisq-ccx-strip-text .squisq-md-p {
|
|
81
|
+
display: inline;
|
|
82
|
+
margin: 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.squisq-ccx-strip-text .squisq-md-strong {
|
|
86
|
+
color: var(--ccx-fg-strong);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* ── Expanded body ───────────────────────────────────────────────── */
|
|
90
|
+
|
|
91
|
+
.squisq-ccx-body {
|
|
92
|
+
padding: 2px 12px 8px 24px;
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
user-select: text;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.squisq-ccx-body--loading {
|
|
98
|
+
opacity: 0.6;
|
|
99
|
+
font-style: italic;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.squisq-ccx-body .squisq-md-p {
|
|
103
|
+
margin: 4px 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.squisq-ccx-body .squisq-md-strong {
|
|
107
|
+
color: var(--ccx-fg-strong);
|
|
108
|
+
font-size: 11px;
|
|
109
|
+
text-transform: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.squisq-ccx-body .squisq-md-list {
|
|
113
|
+
margin: 2px 0 6px;
|
|
114
|
+
padding-left: 18px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.squisq-ccx-body .squisq-md-li {
|
|
118
|
+
margin: 1px 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.squisq-ccx-body .squisq-md-link {
|
|
122
|
+
color: var(--ccx-accent);
|
|
123
|
+
text-decoration: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.squisq-ccx-body .squisq-md-link:hover {
|
|
127
|
+
text-decoration: underline;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.squisq-ccx-body .squisq-md-inline-code,
|
|
131
|
+
.squisq-ccx-body .squisq-md-link .squisq-md-inline-code {
|
|
132
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
133
|
+
font-size: 11px;
|
|
134
|
+
padding: 0 3px;
|
|
135
|
+
border-radius: 3px;
|
|
136
|
+
background: rgba(140, 149, 159, 0.18);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.squisq-ccx-body .squisq-md-link .squisq-md-inline-code {
|
|
140
|
+
color: var(--ccx-accent);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.squisq-ccx-body .squisq-md-code-block {
|
|
144
|
+
margin: 4px 0;
|
|
145
|
+
padding: 6px 8px;
|
|
146
|
+
border-radius: 4px;
|
|
147
|
+
background: rgba(140, 149, 159, 0.14);
|
|
148
|
+
overflow-x: auto;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.squisq-ccx-body .squisq-md-code-block code {
|
|
152
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
153
|
+
font-size: 11px;
|
|
154
|
+
white-space: pre;
|
|
155
|
+
}
|
package/src/styles/editor.css
CHANGED
|
@@ -1416,22 +1416,142 @@
|
|
|
1416
1416
|
|
|
1417
1417
|
/* ─── Preview Controls ──────────────────────────────── */
|
|
1418
1418
|
|
|
1419
|
+
/* Left-side preview switches: the segmented mode switch and the aspect-ratio
|
|
1420
|
+
switch, separated by a divider. Natural width; sits just after the view
|
|
1421
|
+
tabs and never shrinks. */
|
|
1422
|
+
.squisq-preview-left-controls {
|
|
1423
|
+
display: flex;
|
|
1424
|
+
align-items: center;
|
|
1425
|
+
gap: 8px;
|
|
1426
|
+
margin-left: 8px;
|
|
1427
|
+
flex: 0 0 auto;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
.squisq-preview-seg-divider {
|
|
1431
|
+
width: 1px;
|
|
1432
|
+
height: 20px;
|
|
1433
|
+
background: var(--squisq-border, #d1d5db);
|
|
1434
|
+
flex: 0 0 auto;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
/* Segmented switch — a row of connected buttons (display mode: Video /
|
|
1438
|
+
Slideshow / Document / Page; aspect ratio: 16:9 / 1:1 / 9:16 / 4:3),
|
|
1439
|
+
replacing the old "Mode:" / "Format:" dropdowns. */
|
|
1440
|
+
.squisq-preview-seg {
|
|
1441
|
+
display: inline-flex;
|
|
1442
|
+
align-items: center;
|
|
1443
|
+
flex: 0 0 auto;
|
|
1444
|
+
border: 1px solid var(--squisq-border, #d1d5db);
|
|
1445
|
+
border-radius: 6px;
|
|
1446
|
+
overflow: hidden;
|
|
1447
|
+
background: var(--squisq-input-bg, #fff);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
.squisq-preview-seg-btn {
|
|
1451
|
+
appearance: none;
|
|
1452
|
+
border: none;
|
|
1453
|
+
border-left: 1px solid var(--squisq-border, #d1d5db);
|
|
1454
|
+
background: transparent;
|
|
1455
|
+
color: #4b5563;
|
|
1456
|
+
font-size: 12px;
|
|
1457
|
+
font-weight: 500;
|
|
1458
|
+
line-height: 1;
|
|
1459
|
+
padding: 6px 11px;
|
|
1460
|
+
cursor: pointer;
|
|
1461
|
+
white-space: nowrap;
|
|
1462
|
+
transition:
|
|
1463
|
+
background 0.12s,
|
|
1464
|
+
color 0.12s;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
/* Icon segments (aspect ratio) center their glyph and use tighter, square-ish
|
|
1468
|
+
padding so the row of ratio icons reads as a compact cluster. */
|
|
1469
|
+
.squisq-preview-seg-btn--icon {
|
|
1470
|
+
display: flex;
|
|
1471
|
+
align-items: center;
|
|
1472
|
+
justify-content: center;
|
|
1473
|
+
padding: 4px 8px;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
.squisq-preview-seg-btn--icon svg {
|
|
1477
|
+
display: block;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
/* Font Awesome glyph segments (captions: cc / social) — size the webfont
|
|
1481
|
+
glyph to sit level with the SVG aspect-ratio icons. */
|
|
1482
|
+
.squisq-preview-seg-btn--icon > i {
|
|
1483
|
+
font-size: 15px;
|
|
1484
|
+
line-height: 1;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
.squisq-preview-seg-btn:first-child {
|
|
1488
|
+
border-left: none;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
.squisq-preview-seg-btn:hover {
|
|
1492
|
+
background: #f3f4f6;
|
|
1493
|
+
color: #111827;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
.squisq-preview-seg-btn--active,
|
|
1497
|
+
.squisq-preview-seg-btn--active:hover {
|
|
1498
|
+
background: #2563eb;
|
|
1499
|
+
color: #fff;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
/* Flex-filler root: spans the toolbar's leftover width, which is the budget
|
|
1503
|
+
PreviewToolbarControls measures to decide how many controls stay inline vs
|
|
1504
|
+
fold into the overflow menu. It also pushes the right-side toolbar buttons
|
|
1505
|
+
to the far edge (replacing the preview-mode spacer). `position: relative`
|
|
1506
|
+
anchors the hidden measurement probe; the 9px lead offsets the first
|
|
1507
|
+
control from the view-tabs divider. */
|
|
1508
|
+
.squisq-preview-controls {
|
|
1509
|
+
display: flex;
|
|
1510
|
+
align-items: center;
|
|
1511
|
+
gap: 6px;
|
|
1512
|
+
flex: 1 1 auto;
|
|
1513
|
+
min-width: 0;
|
|
1514
|
+
position: relative;
|
|
1515
|
+
padding-left: 9px;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
/* Hidden probe: renders every control at natural width so the fit measurement
|
|
1519
|
+
has per-control widths to work from. Absolutely positioned + hidden so it
|
|
1520
|
+
never affects layout or catches pointer events. */
|
|
1521
|
+
.squisq-preview-controls-probe {
|
|
1522
|
+
position: absolute;
|
|
1523
|
+
top: 0;
|
|
1524
|
+
left: 0;
|
|
1525
|
+
display: flex;
|
|
1526
|
+
align-items: center;
|
|
1527
|
+
gap: 6px;
|
|
1528
|
+
flex-wrap: nowrap;
|
|
1529
|
+
white-space: nowrap;
|
|
1530
|
+
visibility: hidden;
|
|
1531
|
+
pointer-events: none;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/* Inline row sits at its natural width (the overflow gear, when present, sits
|
|
1535
|
+
just after it); it never wraps — controls that don't fit move to the menu. */
|
|
1419
1536
|
.squisq-preview-controls-inline {
|
|
1420
1537
|
display: flex;
|
|
1421
1538
|
align-items: center;
|
|
1422
1539
|
gap: 6px;
|
|
1423
|
-
flex-wrap:
|
|
1424
|
-
|
|
1540
|
+
flex-wrap: nowrap;
|
|
1541
|
+
flex: 0 0 auto;
|
|
1425
1542
|
}
|
|
1426
1543
|
|
|
1427
|
-
.squisq-preview-controls-inline .squisq-preview-control
|
|
1544
|
+
.squisq-preview-controls-inline .squisq-preview-control,
|
|
1545
|
+
.squisq-preview-controls-probe .squisq-preview-control {
|
|
1428
1546
|
display: flex;
|
|
1429
1547
|
align-items: center;
|
|
1430
1548
|
gap: 4px;
|
|
1549
|
+
flex-shrink: 0;
|
|
1431
1550
|
}
|
|
1432
1551
|
|
|
1433
1552
|
.squisq-preview-controls-compact {
|
|
1434
1553
|
position: relative;
|
|
1554
|
+
flex: 0 0 auto;
|
|
1435
1555
|
}
|
|
1436
1556
|
|
|
1437
1557
|
.squisq-preview-controls-popover {
|
|
@@ -1622,6 +1742,32 @@
|
|
|
1622
1742
|
background: #4b5563;
|
|
1623
1743
|
}
|
|
1624
1744
|
|
|
1745
|
+
/* Segmented switches — mode + aspect ratio (dark) */
|
|
1746
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-divider {
|
|
1747
|
+
background: #4b5563;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-preview-seg {
|
|
1751
|
+
border-color: #4b5563;
|
|
1752
|
+
background: #111827;
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-btn {
|
|
1756
|
+
color: #d1d5db;
|
|
1757
|
+
border-left-color: #4b5563;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-btn:hover {
|
|
1761
|
+
background: #374151;
|
|
1762
|
+
color: #f9fafb;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-btn--active,
|
|
1766
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-preview-seg-btn--active:hover {
|
|
1767
|
+
background: #2563eb;
|
|
1768
|
+
color: #fff;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1625
1771
|
/* Overflow menu (dark) */
|
|
1626
1772
|
.squisq-editor-shell[data-theme='dark'] .squisq-toolbar-overflow-menu {
|
|
1627
1773
|
background: #1f2937;
|
package/src/styles/index.css
CHANGED