docblocks-react-ui 0.3.1 → 0.4.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/CHANGELOG.md +102 -0
- package/README.md +1 -0
- package/dist/{Editor-BvKqUGXH.mjs → Editor-BgG_An_T.mjs} +490 -280
- package/dist/{constants-D4FTL0ph.mjs → constants-BaAj3jDA.mjs} +52 -1
- package/dist/{index-DG0lJy_t.d.mts → index-1dJBnGYv.d.mts} +11 -8
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/mermaid/index.mjs +2 -2
- package/dist/pdf/index.d.mts +1 -1
- package/dist/pdf/index.mjs +318 -51
- package/dist/styles.css +54 -0
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as ChevronLeft, c as isSafeDownloadHref, d as cn, i as ChevronRight, l as preventDefault, n as X, o as assertNever, r as TriangleAlert, s as clamp, t as isEmbeddablePdfUrl, u as stopPropagation } from "./constants-BaAj3jDA.mjs";
|
|
2
2
|
import { useStore } from "zustand";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Component, Fragment, createContext, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
@@ -2023,40 +2023,6 @@ function ChevronDown(props) {
|
|
|
2023
2023
|
});
|
|
2024
2024
|
}
|
|
2025
2025
|
//#endregion
|
|
2026
|
-
//#region src/shared/ui/icons/ChevronLeft.tsx
|
|
2027
|
-
function ChevronLeft(props) {
|
|
2028
|
-
return /* @__PURE__ */ jsx("svg", {
|
|
2029
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
2030
|
-
width: 24,
|
|
2031
|
-
height: 24,
|
|
2032
|
-
viewBox: "0 0 24 24",
|
|
2033
|
-
fill: "none",
|
|
2034
|
-
stroke: "currentColor",
|
|
2035
|
-
strokeWidth: 2,
|
|
2036
|
-
strokeLinecap: "round",
|
|
2037
|
-
strokeLinejoin: "round",
|
|
2038
|
-
...props,
|
|
2039
|
-
children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" })
|
|
2040
|
-
});
|
|
2041
|
-
}
|
|
2042
|
-
//#endregion
|
|
2043
|
-
//#region src/shared/ui/icons/ChevronRight.tsx
|
|
2044
|
-
function ChevronRight(props) {
|
|
2045
|
-
return /* @__PURE__ */ jsx("svg", {
|
|
2046
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
2047
|
-
width: 24,
|
|
2048
|
-
height: 24,
|
|
2049
|
-
viewBox: "0 0 24 24",
|
|
2050
|
-
fill: "none",
|
|
2051
|
-
stroke: "currentColor",
|
|
2052
|
-
strokeWidth: 2,
|
|
2053
|
-
strokeLinecap: "round",
|
|
2054
|
-
strokeLinejoin: "round",
|
|
2055
|
-
...props,
|
|
2056
|
-
children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
|
|
2057
|
-
});
|
|
2058
|
-
}
|
|
2059
|
-
//#endregion
|
|
2060
2026
|
//#region src/shared/ui/icons/ChevronUp.tsx
|
|
2061
2027
|
function ChevronUp(props) {
|
|
2062
2028
|
return /* @__PURE__ */ jsx("svg", {
|
|
@@ -3767,23 +3733,6 @@ function Underline(props) {
|
|
|
3767
3733
|
});
|
|
3768
3734
|
}
|
|
3769
3735
|
//#endregion
|
|
3770
|
-
//#region src/shared/ui/icons/X.tsx
|
|
3771
|
-
function X(props) {
|
|
3772
|
-
return /* @__PURE__ */ jsxs("svg", {
|
|
3773
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
3774
|
-
width: 24,
|
|
3775
|
-
height: 24,
|
|
3776
|
-
viewBox: "0 0 24 24",
|
|
3777
|
-
fill: "none",
|
|
3778
|
-
stroke: "currentColor",
|
|
3779
|
-
strokeWidth: 2,
|
|
3780
|
-
strokeLinecap: "round",
|
|
3781
|
-
strokeLinejoin: "round",
|
|
3782
|
-
...props,
|
|
3783
|
-
children: [/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }), /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })]
|
|
3784
|
-
});
|
|
3785
|
-
}
|
|
3786
|
-
//#endregion
|
|
3787
3736
|
//#region src/shared/ui/icons/Zap.tsx
|
|
3788
3737
|
function Zap(props) {
|
|
3789
3738
|
return /* @__PURE__ */ jsx("svg", {
|
|
@@ -4137,7 +4086,11 @@ var blocks_default$1 = {
|
|
|
4137
4086
|
"wrap": "Wrap long lines",
|
|
4138
4087
|
"rendering": "rendering…",
|
|
4139
4088
|
"pdfPreviewUnavailable": "PDF preview unavailable ({error}). Showing the built-in viewer.",
|
|
4140
|
-
"pdfLoading": "loading preview…"
|
|
4089
|
+
"pdfLoading": "loading preview…",
|
|
4090
|
+
"pdfPrevPage": "Previous page",
|
|
4091
|
+
"pdfNextPage": "Next page",
|
|
4092
|
+
"pdfFullscreen": "Expand to fullscreen",
|
|
4093
|
+
"pdfCloseFullscreen": "Close fullscreen"
|
|
4141
4094
|
},
|
|
4142
4095
|
image: {
|
|
4143
4096
|
"placeholder": "image",
|
|
@@ -4149,7 +4102,13 @@ var blocks_default$1 = {
|
|
|
4149
4102
|
embed: {
|
|
4150
4103
|
"zoomIn": "Zoom in",
|
|
4151
4104
|
"zoomOut": "Zoom out",
|
|
4152
|
-
"zoomReset": "Reset zoom"
|
|
4105
|
+
"zoomReset": "Reset zoom",
|
|
4106
|
+
"resizeHeight": "Resize embed height",
|
|
4107
|
+
"editUrl": "Edit URL",
|
|
4108
|
+
"saveUrl": "Save URL",
|
|
4109
|
+
"cancelEdit": "Cancel",
|
|
4110
|
+
"srcPlaceholder": "Embed URL (https://…)",
|
|
4111
|
+
"unsafeHint": "Only absolute http(s):// URLs are supported."
|
|
4153
4112
|
},
|
|
4154
4113
|
syncEmbed: {
|
|
4155
4114
|
"broken": "The master document for this synced block is unavailable.",
|
|
@@ -4479,7 +4438,7 @@ var slash_default$1 = {
|
|
|
4479
4438
|
"code": "Code block",
|
|
4480
4439
|
"codeMermaid": "Code block (mermaid)",
|
|
4481
4440
|
"divider": "Divider",
|
|
4482
|
-
"
|
|
4441
|
+
"file": "File",
|
|
4483
4442
|
"embed": "Embed",
|
|
4484
4443
|
"database": "Database",
|
|
4485
4444
|
"frame": "Frame",
|
|
@@ -4507,7 +4466,7 @@ var slash_default$1 = {
|
|
|
4507
4466
|
},
|
|
4508
4467
|
embed: {
|
|
4509
4468
|
"urlPlaceholder": "Embed URL (https://…)",
|
|
4510
|
-
"unsafeHint": "Only absolute
|
|
4469
|
+
"unsafeHint": "Only absolute http(s):// URLs are supported."
|
|
4511
4470
|
}
|
|
4512
4471
|
};
|
|
4513
4472
|
//#endregion
|
|
@@ -4579,7 +4538,11 @@ var blocks_default = {
|
|
|
4579
4538
|
"wrap": "Переносить длинные строки",
|
|
4580
4539
|
"rendering": "Рисование…",
|
|
4581
4540
|
"pdfPreviewUnavailable": "Превью PDF недоступно ({error}). Показан встроенный просмотрщик.",
|
|
4582
|
-
"pdfLoading": "Загрузка превью…"
|
|
4541
|
+
"pdfLoading": "Загрузка превью…",
|
|
4542
|
+
"pdfPrevPage": "Предыдущая страница",
|
|
4543
|
+
"pdfNextPage": "Следующая страница",
|
|
4544
|
+
"pdfFullscreen": "Развернуть на весь экран",
|
|
4545
|
+
"pdfCloseFullscreen": "Закрыть полноэкранный режим"
|
|
4583
4546
|
},
|
|
4584
4547
|
image: {
|
|
4585
4548
|
"placeholder": "изображение",
|
|
@@ -4591,7 +4554,13 @@ var blocks_default = {
|
|
|
4591
4554
|
embed: {
|
|
4592
4555
|
"zoomIn": "Приблизить",
|
|
4593
4556
|
"zoomOut": "Отдалить",
|
|
4594
|
-
"zoomReset": "Сбросить масштаб"
|
|
4557
|
+
"zoomReset": "Сбросить масштаб",
|
|
4558
|
+
"resizeHeight": "Изменить высоту встраивания",
|
|
4559
|
+
"editUrl": "Изменить ссылку",
|
|
4560
|
+
"saveUrl": "Сохранить ссылку",
|
|
4561
|
+
"cancelEdit": "Отмена",
|
|
4562
|
+
"srcPlaceholder": "URL встраивания (https://…)",
|
|
4563
|
+
"unsafeHint": "Поддерживаются только абсолютные URL http(s)://."
|
|
4595
4564
|
},
|
|
4596
4565
|
syncEmbed: {
|
|
4597
4566
|
"broken": "Мастер-документ для этого синхронизируемого блока недоступен.",
|
|
@@ -4921,7 +4890,7 @@ var slash_default = {
|
|
|
4921
4890
|
"code": "Блок кода",
|
|
4922
4891
|
"codeMermaid": "Блок кода (mermaid)",
|
|
4923
4892
|
"divider": "Разделитель",
|
|
4924
|
-
"
|
|
4893
|
+
"file": "Файл",
|
|
4925
4894
|
"embed": "Встраивание",
|
|
4926
4895
|
"database": "База данных",
|
|
4927
4896
|
"frame": "Фрейм",
|
|
@@ -4949,7 +4918,7 @@ var slash_default = {
|
|
|
4949
4918
|
},
|
|
4950
4919
|
embed: {
|
|
4951
4920
|
"urlPlaceholder": "URL встраивания (https://…)",
|
|
4952
|
-
"unsafeHint": "
|
|
4921
|
+
"unsafeHint": "Поддерживаются только абсолютные URL http(s)://."
|
|
4953
4922
|
}
|
|
4954
4923
|
};
|
|
4955
4924
|
//#endregion
|
|
@@ -10393,22 +10362,16 @@ function FrameSlashPicker({ onCommit, onCancel }) {
|
|
|
10393
10362
|
//#endregion
|
|
10394
10363
|
//#region src/editing/slash/SlashEmbedForm.tsx
|
|
10395
10364
|
/**
|
|
10396
|
-
* The slash `Embed` form (the embed-creation affordance):
|
|
10397
|
-
*
|
|
10398
|
-
* render as an iframe
|
|
10399
|
-
*
|
|
10400
|
-
*
|
|
10401
|
-
*
|
|
10402
|
-
*
|
|
10403
|
-
*/
|
|
10404
|
-
/** The embed's kind
|
|
10405
|
-
const
|
|
10406
|
-
"iframe",
|
|
10407
|
-
"github",
|
|
10408
|
-
"figma",
|
|
10409
|
-
"loom",
|
|
10410
|
-
"html"
|
|
10411
|
-
];
|
|
10365
|
+
* The slash `Embed` form (the embed-creation affordance): a URL input
|
|
10366
|
+
* (auto-focused) only — the kind stands the `iframe` default (the renderer's
|
|
10367
|
+
* hint; all kinds render as an iframe). The menu stands open over the phrase
|
|
10368
|
+
* while the form stands; the commit (the URL's Enter, or the check) lands the
|
|
10369
|
+
* phrase strip + the `insertBlock('embed', { kind: "iframe", src })` in the
|
|
10370
|
+
* caller's transact. An empty (or whitespace-only) URL commits nothing (the
|
|
10371
|
+
* menu stands open on the phrase — the Image / Database contract).
|
|
10372
|
+
*/
|
|
10373
|
+
/** The embed's kind (the renderer's hint — the form stands the URL only). */
|
|
10374
|
+
const EMBED_KIND_IFRAME = "iframe";
|
|
10412
10375
|
/** The form's i18n scope: the package's instance stands the provider's seat
|
|
10413
10376
|
* over the form (the form stands also outside the editor's scope). */
|
|
10414
10377
|
function SlashEmbedForm(props) {
|
|
@@ -10418,7 +10381,6 @@ function SlashEmbedForm(props) {
|
|
|
10418
10381
|
function SlashEmbedFormInner({ onCommit, onCancel }) {
|
|
10419
10382
|
const { t } = useTranslation("slash");
|
|
10420
10383
|
const [url, setUrl] = useState("");
|
|
10421
|
-
const [kind, setKind] = useState("iframe");
|
|
10422
10384
|
const urlInputRef = useRef(null);
|
|
10423
10385
|
useEffect(() => {
|
|
10424
10386
|
urlInputRef.current?.focus();
|
|
@@ -10426,18 +10388,15 @@ function SlashEmbedFormInner({ onCommit, onCancel }) {
|
|
|
10426
10388
|
const commit = () => {
|
|
10427
10389
|
const trimmed = url.trim();
|
|
10428
10390
|
if (trimmed.length === 0) return;
|
|
10429
|
-
if (!isSafeEmbedSrc(
|
|
10430
|
-
onCommit(
|
|
10431
|
-
kind,
|
|
10432
|
-
src: trimmed
|
|
10433
|
-
});
|
|
10391
|
+
if (!isSafeEmbedSrc(EMBED_KIND_IFRAME, trimmed)) return;
|
|
10392
|
+
onCommit(trimmed);
|
|
10434
10393
|
};
|
|
10435
10394
|
const stopFormKey = (event) => {
|
|
10436
10395
|
event.stopPropagation();
|
|
10437
10396
|
if (event.key === "Escape") onCancel();
|
|
10438
10397
|
};
|
|
10439
|
-
const canCommit = url.trim().length > 0 && isSafeEmbedSrc(
|
|
10440
|
-
const unsafeHint = url.trim().length > 0 && !isSafeEmbedSrc(
|
|
10398
|
+
const canCommit = url.trim().length > 0 && isSafeEmbedSrc(EMBED_KIND_IFRAME, url.trim());
|
|
10399
|
+
const unsafeHint = url.trim().length > 0 && !isSafeEmbedSrc(EMBED_KIND_IFRAME, url.trim()) ? t("embed.unsafeHint") : null;
|
|
10441
10400
|
const onUrlKeyDown = (event) => {
|
|
10442
10401
|
stopFormKey(event);
|
|
10443
10402
|
if (event.key === "Enter") {
|
|
@@ -10446,51 +10405,36 @@ function SlashEmbedFormInner({ onCommit, onCancel }) {
|
|
|
10446
10405
|
}
|
|
10447
10406
|
};
|
|
10448
10407
|
const onUrlChange = (event) => setUrl(event.target.value);
|
|
10449
|
-
const onKindChange = (event) => setKind(event.target.value);
|
|
10450
10408
|
return /* @__PURE__ */ jsxs("div", {
|
|
10451
10409
|
"data-slot": "docblocks/slash-embed-form",
|
|
10452
10410
|
className: "flex w-56 flex-col gap-2 p-1",
|
|
10453
|
-
children: [
|
|
10454
|
-
|
|
10411
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
10412
|
+
className: "flex items-center gap-1",
|
|
10413
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
10455
10414
|
size: "sm",
|
|
10456
10415
|
ref: urlInputRef,
|
|
10457
10416
|
type: "text",
|
|
10458
10417
|
"data-slot": "docblocks/slash-embed-url",
|
|
10459
|
-
className: "rounded-sm",
|
|
10418
|
+
className: "h-7 flex-1 rounded-sm",
|
|
10460
10419
|
placeholder: t("embed.urlPlaceholder"),
|
|
10461
10420
|
value: url,
|
|
10462
10421
|
onKeyDown: onUrlKeyDown,
|
|
10463
10422
|
onChange: onUrlChange
|
|
10464
|
-
}),
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10472
|
-
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
variant: "outline",
|
|
10480
|
-
size: "icon",
|
|
10481
|
-
className: "size-7 cursor-default rounded-sm bg-transparent disabled:cursor-default dark:bg-transparent",
|
|
10482
|
-
disabled: !canCommit,
|
|
10483
|
-
onMouseDown: preventDefault,
|
|
10484
|
-
onClick: commit,
|
|
10485
|
-
children: /* @__PURE__ */ jsx(Check, { className: "size-4" })
|
|
10486
|
-
})]
|
|
10487
|
-
}),
|
|
10488
|
-
unsafeHint !== null && /* @__PURE__ */ jsx("p", {
|
|
10489
|
-
"data-slot": "docblocks/slash-embed-hint",
|
|
10490
|
-
className: "px-1 text-xs text-destructive",
|
|
10491
|
-
children: unsafeHint
|
|
10492
|
-
})
|
|
10493
|
-
]
|
|
10423
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
10424
|
+
"data-slot": "docblocks/slash-embed-insert",
|
|
10425
|
+
variant: "outline",
|
|
10426
|
+
size: "icon",
|
|
10427
|
+
className: "\n size-7 shrink-0 cursor-default rounded-sm bg-transparent\n disabled:cursor-default\n dark:bg-transparent\n ",
|
|
10428
|
+
disabled: !canCommit,
|
|
10429
|
+
onMouseDown: preventDefault,
|
|
10430
|
+
onClick: commit,
|
|
10431
|
+
children: /* @__PURE__ */ jsx(Check, { className: "size-4" })
|
|
10432
|
+
})]
|
|
10433
|
+
}), unsafeHint !== null && /* @__PURE__ */ jsx("p", {
|
|
10434
|
+
"data-slot": "docblocks/slash-embed-hint",
|
|
10435
|
+
className: "px-1 text-xs text-destructive",
|
|
10436
|
+
children: unsafeHint
|
|
10437
|
+
})]
|
|
10494
10438
|
});
|
|
10495
10439
|
}
|
|
10496
10440
|
/** The v1 command list (filterable by the typed query). The `label` is the
|
|
@@ -10577,8 +10521,8 @@ const SLASH_COMMANDS = [
|
|
|
10577
10521
|
type: "divider"
|
|
10578
10522
|
},
|
|
10579
10523
|
{
|
|
10580
|
-
label: "
|
|
10581
|
-
labelKey: "blocks.
|
|
10524
|
+
label: "File",
|
|
10525
|
+
labelKey: "blocks.file",
|
|
10582
10526
|
icon: Image,
|
|
10583
10527
|
type: "image"
|
|
10584
10528
|
},
|
|
@@ -11263,7 +11207,7 @@ function useSlashMenu(rootRef) {
|
|
|
11263
11207
|
doc,
|
|
11264
11208
|
rootRef
|
|
11265
11209
|
]);
|
|
11266
|
-
const commitEmbed = useCallback((
|
|
11210
|
+
const commitEmbed = useCallback((src) => {
|
|
11267
11211
|
const pending = pendingEmbedRef.current;
|
|
11268
11212
|
pendingEmbedRef.current = null;
|
|
11269
11213
|
setEmbedForm(false);
|
|
@@ -11284,8 +11228,8 @@ function useSlashMenu(rootRef) {
|
|
|
11284
11228
|
doc.deleteText(blockId, slashOffset, queryEnd);
|
|
11285
11229
|
}
|
|
11286
11230
|
const embedId = doc.insertBlock("embed", {
|
|
11287
|
-
kind:
|
|
11288
|
-
src
|
|
11231
|
+
kind: "iframe",
|
|
11232
|
+
src,
|
|
11289
11233
|
...ind
|
|
11290
11234
|
}, {
|
|
11291
11235
|
parent: parentId,
|
|
@@ -11897,15 +11841,18 @@ function NotPublishedPlaceholder() {
|
|
|
11897
11841
|
//#endregion
|
|
11898
11842
|
//#region src/editor-provider/constants.ts
|
|
11899
11843
|
/**
|
|
11900
|
-
* The
|
|
11901
|
-
*
|
|
11902
|
-
*
|
|
11903
|
-
*
|
|
11904
|
-
*
|
|
11905
|
-
*
|
|
11906
|
-
*
|
|
11907
|
-
|
|
11908
|
-
|
|
11844
|
+
* The embed sandbox (the default when no `embedSandbox` policy is supplied):
|
|
11845
|
+
* the embedded content runs with its own origin's grants (`allow-same-origin`
|
|
11846
|
+
* — the real-app requirement: a dashboard's own storage / cookies stand,
|
|
11847
|
+
* so the app boots like in a plain tab) + scripts, forms, and popups.
|
|
11848
|
+
* The frame's document stands a separate document either way: a
|
|
11849
|
+
* cross-origin embed cannot reach the parent's DOM or state. The one
|
|
11850
|
+
* posture consequence: a same-origin URL (the page's own origin) reaches
|
|
11851
|
+
* the parent over `window.parent` — the document author's own choice to
|
|
11852
|
+
* embed it. The `embedSandbox` policy stands the restriction (the
|
|
11853
|
+
* opaque-origin stand — no `allow-same-origin` — for the untrusted embeds).
|
|
11854
|
+
*/
|
|
11855
|
+
const DEFAULT_EMBED_SANDBOX = "allow-scripts allow-same-origin allow-forms allow-popups";
|
|
11909
11856
|
const defaultEmbedSandbox = () => DEFAULT_EMBED_SANDBOX;
|
|
11910
11857
|
//#endregion
|
|
11911
11858
|
//#region src/editor-provider/context.ts
|
|
@@ -12738,20 +12685,34 @@ function DatabaseBlock({ id, block }) {
|
|
|
12738
12685
|
* only (all kinds render as an iframe; per-kind card renderers come later),
|
|
12739
12686
|
* so an unknown kind (newer data) still renders the same iframe. `src` is a
|
|
12740
12687
|
* URL, not a blob key: the consumer's blob resolver does not apply. The
|
|
12741
|
-
* iframe stands a `sandbox` attribute — the
|
|
12742
|
-
*
|
|
12743
|
-
*
|
|
12744
|
-
*
|
|
12745
|
-
*
|
|
12688
|
+
* iframe stands a `sandbox` attribute — the default grants the same-origin
|
|
12689
|
+
* (the embedded app's own storage / cookies stand, so the real apps boot
|
|
12690
|
+
* like in a plain tab) or the consumer's `embedSandbox` policy (the
|
|
12691
|
+
* opaque-origin's restriction over the untrusted, `null` for the fully
|
|
12692
|
+
* trusted). The block is not text-editable, so the wrapper carries no gutter
|
|
12746
12693
|
* handle.
|
|
12747
12694
|
*
|
|
12748
|
-
*
|
|
12695
|
+
* Height (the stretched stand): the wrapper's height stands the
|
|
12696
|
+
* `--docblocks-embed-height` token (the default — the minimum). The
|
|
12697
|
+
* bottom-edge resize handle (the hover reveal, the keyboard's arrows)
|
|
12698
|
+
* stretches it over the minimum; the stretched px stand the block's
|
|
12699
|
+
* `height` attr (the null idiom: null / absent = the token's default), so
|
|
12700
|
+
* the reload restores it and the panes share it over the wire (the commit
|
|
12701
|
+
* stands the gesture's rest — one write per drag, one per key press).
|
|
12702
|
+
*
|
|
12703
|
+
* Pan/zoom (the persisted view): the iframe stands a CSS
|
|
12749
12704
|
* `translate(x, y) scale(s)` transform (the wrapper clips the overflow). The
|
|
12750
|
-
*
|
|
12751
|
-
*
|
|
12752
|
-
*
|
|
12705
|
+
* view stands the block's `zoom` / `panX` / `panY` attrs (the null idiom:
|
|
12706
|
+
* null / absent = the identity) — the reload restores the view and the wire
|
|
12707
|
+
* carries it over the panes (the panes share the persisted view; a pane's
|
|
12708
|
+
* gesture writes over the doc, the others adopt it). The write is debounced
|
|
12709
|
+
* over the gesture's bursts (one doc write per rest). The controls:
|
|
12753
12710
|
*
|
|
12754
12711
|
* - the hover toolbar (zoom in / out, the percent, reset) — always stands;
|
|
12712
|
+
* - the toolbar's URL edit (the editing mode's stand): the panel over the
|
|
12713
|
+
* toolbar changes the `src` (the safe non-empty's write — the unsafe's
|
|
12714
|
+
* rejection over the slash form's rule; the iframe's navigation over the
|
|
12715
|
+
* new src, the view's / the height's attrs stand untouched);
|
|
12755
12716
|
* - the wheel — `ctrl`/`cmd` zooms at the cursor, a plain wheel pans
|
|
12756
12717
|
* vertically (content opposite to the delta, the canvas idiom);
|
|
12757
12718
|
* - the modifier drag — a `space` / `alt`-held pointer drag pans (the iframe
|
|
@@ -12771,12 +12732,36 @@ const EMBED_MAX_SCALE = 4;
|
|
|
12771
12732
|
const EMBED_ZOOM_STEP = 1.2;
|
|
12772
12733
|
/** The wheel's continuous zoom factor (the exponential of the delta, the canvas pinch idiom). */
|
|
12773
12734
|
const EMBED_WHEEL_ZOOM = .002;
|
|
12735
|
+
/** The view's persist debounce (the gesture's bursts stand one doc write per rest). */
|
|
12736
|
+
const EMBED_PERSIST_DEBOUNCE_MS = 250;
|
|
12737
|
+
/** The height's fallback minimum (px): the `--docblocks-embed-height` token's default (the non-CSS environment's stand — jsdom). */
|
|
12738
|
+
const EMBED_MIN_HEIGHT = 300;
|
|
12739
|
+
/** The resize handle's key step (px per arrow press). */
|
|
12740
|
+
const EMBED_RESIZE_KEY_STEP = 16;
|
|
12774
12741
|
/** The identity view: the iframe at its natural size, filling the wrapper. */
|
|
12775
12742
|
const EMBED_VIEW_IDENTITY = {
|
|
12776
12743
|
scale: 1,
|
|
12777
12744
|
x: 0,
|
|
12778
12745
|
y: 0
|
|
12779
12746
|
};
|
|
12747
|
+
/** The view over the block's persisted attrs (the null idiom's restore). */
|
|
12748
|
+
function viewOfAttrs(zoom, panX, panY) {
|
|
12749
|
+
return {
|
|
12750
|
+
scale: zoom ?? 1,
|
|
12751
|
+
x: panX ?? 0,
|
|
12752
|
+
y: panY ?? 0
|
|
12753
|
+
};
|
|
12754
|
+
}
|
|
12755
|
+
/**
|
|
12756
|
+
* The view's wire stand: the gesture's floating-point's residue must not
|
|
12757
|
+
* stand a non-identity's persist (the zoom-out/in's round-trip lands the
|
|
12758
|
+
* 0.9999…'s "identity"). The zoom's round to the label's 1%'s precision,
|
|
12759
|
+
* the pan's to the 0.1 px (the sub-pixel's residue stands the identity).
|
|
12760
|
+
*/
|
|
12761
|
+
function roundViewValue(value, places) {
|
|
12762
|
+
const factor = 10 ** places;
|
|
12763
|
+
return Math.round(value * factor) / factor;
|
|
12764
|
+
}
|
|
12780
12765
|
/**
|
|
12781
12766
|
* Clamp the pan against the scaled embed's overflow. The embed cannot be
|
|
12782
12767
|
* dragged past the container: at `scale <= 1` it fits (or is smaller) and
|
|
@@ -12802,17 +12787,61 @@ function zoomAtPoint(view, cx, cy, nextScale) {
|
|
|
12802
12787
|
y: cy - (cy - view.y) * factor
|
|
12803
12788
|
};
|
|
12804
12789
|
}
|
|
12790
|
+
/**
|
|
12791
|
+
* The height's wire stand: the token's minimum over the wrapper (the
|
|
12792
|
+
* themed token's stand), the fallback's default over the non-CSS's stand
|
|
12793
|
+
* (jsdom's empty computed value).
|
|
12794
|
+
*/
|
|
12795
|
+
function readEmbedMinHeight(wrapper) {
|
|
12796
|
+
const raw = getComputedStyle(wrapper).getPropertyValue("--docblocks-embed-height");
|
|
12797
|
+
const value = Number.parseFloat(raw);
|
|
12798
|
+
return Number.isFinite(value) && value > 0 ? value : EMBED_MIN_HEIGHT;
|
|
12799
|
+
}
|
|
12805
12800
|
function EmbedBlock({ id, block }) {
|
|
12806
12801
|
const { t } = useTranslation("blocks");
|
|
12807
12802
|
const { kind, src, title } = block.attrs;
|
|
12808
|
-
const { embedSandbox } = useEditorContext();
|
|
12803
|
+
const { doc, embedSandbox, viewMode } = useEditorContext();
|
|
12809
12804
|
const sandbox = embedSandbox(src);
|
|
12810
12805
|
const wrapperRef = useRef(null);
|
|
12811
|
-
const [view, setView] = useState(
|
|
12806
|
+
const [view, setView] = useState(() => viewOfAttrs(block.attrs.zoom, block.attrs.panX, block.attrs.panY));
|
|
12812
12807
|
const viewRef = useRef(view);
|
|
12813
12808
|
viewRef.current = view;
|
|
12809
|
+
const [height, setHeight] = useState(block.attrs.height ?? null);
|
|
12810
|
+
const heightRef = useRef(height);
|
|
12811
|
+
heightRef.current = height;
|
|
12812
|
+
const [tokenMin, setTokenMin] = useState(EMBED_MIN_HEIGHT);
|
|
12813
|
+
const resizeSession = useRef(null);
|
|
12814
12814
|
const [panModifier, setPanModifier] = useState(false);
|
|
12815
12815
|
const panSession = useRef(null);
|
|
12816
|
+
const [editOpen, setEditOpen] = useState(false);
|
|
12817
|
+
const [draft, setDraft] = useState(src);
|
|
12818
|
+
const editInputRef = useRef(null);
|
|
12819
|
+
/** The edit's open: the draft stands the block's own src (the cancel's restore). */
|
|
12820
|
+
const openEdit = () => {
|
|
12821
|
+
setDraft(src);
|
|
12822
|
+
setEditOpen(true);
|
|
12823
|
+
};
|
|
12824
|
+
const closeEdit = () => setEditOpen(false);
|
|
12825
|
+
useEffect(() => {
|
|
12826
|
+
if (editOpen) editInputRef.current?.focus();
|
|
12827
|
+
}, [editOpen]);
|
|
12828
|
+
const trimmedDraft = draft.trim();
|
|
12829
|
+
const canSave = trimmedDraft.length > 0 && isSafeEmbedSrc(kind, trimmedDraft);
|
|
12830
|
+
const editHint = trimmedDraft.length > 0 && !isSafeEmbedSrc(kind, trimmedDraft) ? t("embed.unsafeHint") : null;
|
|
12831
|
+
/** The edit's commit: the safe non-empty draft stands the `src`'s write.
|
|
12832
|
+
* The unchanged draft writes nothing (no updatedAt's stamp, no wire update). */
|
|
12833
|
+
const commitEdit = () => {
|
|
12834
|
+
if (!canSave) return;
|
|
12835
|
+
if (trimmedDraft !== src) doc.updateBlock(id, { src: trimmedDraft });
|
|
12836
|
+
setEditOpen(false);
|
|
12837
|
+
};
|
|
12838
|
+
const onEditKeyDown = (event) => {
|
|
12839
|
+
event.stopPropagation();
|
|
12840
|
+
if (event.key === "Enter") {
|
|
12841
|
+
event.preventDefault();
|
|
12842
|
+
commitEdit();
|
|
12843
|
+
} else if (event.key === "Escape") closeEdit();
|
|
12844
|
+
};
|
|
12816
12845
|
useEffect(() => {
|
|
12817
12846
|
const isPanModifier = (event) => event.code === "Space" || event.key === "Alt";
|
|
12818
12847
|
const onKeyDown = (event) => {
|
|
@@ -12879,6 +12908,125 @@ function EmbedBlock({ id, block }) {
|
|
|
12879
12908
|
const zoomOut = () => zoomBy(1 / EMBED_ZOOM_STEP);
|
|
12880
12909
|
const resetView = () => setView(EMBED_VIEW_IDENTITY);
|
|
12881
12910
|
const atIdentity = view.scale === EMBED_VIEW_IDENTITY.scale && view.x === 0 && view.y === 0;
|
|
12911
|
+
/**
|
|
12912
|
+
* The persisted view's write (the debounce over the gesture's bursts): the
|
|
12913
|
+
* non-identity view stands the block's attrs (the zoom / panX / panY — the
|
|
12914
|
+
* null idiom: the identity parts stand `null`, the reader's restore), so
|
|
12915
|
+
* the reload / the new tab's embed stands the same view. The values stand
|
|
12916
|
+
* the wire's precision (the 1%'s zoom, the 0.1 px's pan — the rounding's
|
|
12917
|
+
* guard over the floating-point's residue). The no-op stand: a view equal
|
|
12918
|
+
* to the block's own attrs writes nothing (no updatedAt's stamp, no wire
|
|
12919
|
+
* update).
|
|
12920
|
+
*/
|
|
12921
|
+
useEffect(() => {
|
|
12922
|
+
const zoom = roundViewValue(view.scale, 2);
|
|
12923
|
+
const x = roundViewValue(view.x, 1);
|
|
12924
|
+
const y = roundViewValue(view.y, 1);
|
|
12925
|
+
if (zoom === (block.attrs.zoom ?? 1) && x === (block.attrs.panX ?? 0) && y === (block.attrs.panY ?? 0)) return;
|
|
12926
|
+
const timer = window.setTimeout(() => {
|
|
12927
|
+
doc.updateBlock(id, {
|
|
12928
|
+
zoom: zoom === 1 ? null : zoom,
|
|
12929
|
+
panX: x === 0 ? null : x,
|
|
12930
|
+
panY: y === 0 ? null : y
|
|
12931
|
+
});
|
|
12932
|
+
}, EMBED_PERSIST_DEBOUNCE_MS);
|
|
12933
|
+
return () => window.clearTimeout(timer);
|
|
12934
|
+
}, [
|
|
12935
|
+
view,
|
|
12936
|
+
block.attrs,
|
|
12937
|
+
doc,
|
|
12938
|
+
id
|
|
12939
|
+
]);
|
|
12940
|
+
/**
|
|
12941
|
+
* The remote's stand: the panes share the persisted view over the wire — a
|
|
12942
|
+
* pane's peer lands a view different from the local's (the block's attrs
|
|
12943
|
+
* stand the peer's write), so the local's view adopts it. Own writes land
|
|
12944
|
+
* equal to the local's view and stand no adopt. The adopt clamps the
|
|
12945
|
+
* restored pan to the current wrapper (a stale pan over a narrower pane's
|
|
12946
|
+
* clip must not blank the embed).
|
|
12947
|
+
*/
|
|
12948
|
+
useEffect(() => {
|
|
12949
|
+
const scale = block.attrs.zoom ?? 1;
|
|
12950
|
+
const x = block.attrs.panX ?? 0;
|
|
12951
|
+
const y = block.attrs.panY ?? 0;
|
|
12952
|
+
const v = viewRef.current;
|
|
12953
|
+
if (scale === v.scale && x === v.x && y === v.y) return;
|
|
12954
|
+
const rect = wrapperRef.current?.getBoundingClientRect();
|
|
12955
|
+
const clamped = rect === void 0 || rect.width === 0 || rect.height === 0 ? {
|
|
12956
|
+
x,
|
|
12957
|
+
y
|
|
12958
|
+
} : clampEmbedPan(scale, x, y, rect.width, rect.height);
|
|
12959
|
+
setView({
|
|
12960
|
+
scale,
|
|
12961
|
+
x: clamped.x,
|
|
12962
|
+
y: clamped.y
|
|
12963
|
+
});
|
|
12964
|
+
}, [block.attrs]);
|
|
12965
|
+
/** The token's minimum's measure (the themed's stand) over the mount. */
|
|
12966
|
+
useLayoutEffect(() => {
|
|
12967
|
+
const wrapper = wrapperRef.current;
|
|
12968
|
+
if (wrapper === null) return;
|
|
12969
|
+
setTokenMin(readEmbedMinHeight(wrapper));
|
|
12970
|
+
}, []);
|
|
12971
|
+
/**
|
|
12972
|
+
* The remote's stand over the height: the panes share the stretched stand
|
|
12973
|
+
* over the wire — a pane's peer lands a height different from the local's
|
|
12974
|
+
* (the block's `height` attr's stand the peer's write), so the local's
|
|
12975
|
+
* state adopts it. Own commits land equal to the local's state and stand
|
|
12976
|
+
* no adopt.
|
|
12977
|
+
*/
|
|
12978
|
+
useEffect(() => {
|
|
12979
|
+
const attrHeight = block.attrs.height ?? null;
|
|
12980
|
+
if (attrHeight === heightRef.current) return;
|
|
12981
|
+
setHeight(attrHeight);
|
|
12982
|
+
}, [block.attrs]);
|
|
12983
|
+
const onResizePointerDown = (event) => {
|
|
12984
|
+
if (event.button !== 0) return;
|
|
12985
|
+
event.stopPropagation();
|
|
12986
|
+
const wrapper = wrapperRef.current;
|
|
12987
|
+
if (wrapper === null) return;
|
|
12988
|
+
const min = readEmbedMinHeight(wrapper);
|
|
12989
|
+
const rect = wrapper.getBoundingClientRect();
|
|
12990
|
+
event.target.setPointerCapture?.(event.pointerId);
|
|
12991
|
+
resizeSession.current = {
|
|
12992
|
+
pointerId: event.pointerId,
|
|
12993
|
+
startY: event.clientY,
|
|
12994
|
+
startHeight: rect.height,
|
|
12995
|
+
min,
|
|
12996
|
+
lastHeight: rect.height
|
|
12997
|
+
};
|
|
12998
|
+
};
|
|
12999
|
+
const onResizePointerMove = (event) => {
|
|
13000
|
+
const session = resizeSession.current;
|
|
13001
|
+
if (session === null || event.pointerId !== session.pointerId) return;
|
|
13002
|
+
const next = Math.max(session.startHeight + (event.clientY - session.startY), session.min);
|
|
13003
|
+
session.lastHeight = next;
|
|
13004
|
+
setHeight(next);
|
|
13005
|
+
};
|
|
13006
|
+
const endResize = (event) => {
|
|
13007
|
+
const session = resizeSession.current;
|
|
13008
|
+
if (session === null || event.pointerId !== session.pointerId) return;
|
|
13009
|
+
event.target.releasePointerCapture?.(event.pointerId);
|
|
13010
|
+
resizeSession.current = null;
|
|
13011
|
+
const committed = Math.round(session.lastHeight);
|
|
13012
|
+
if (committed <= session.min) {
|
|
13013
|
+
if ((block.attrs.height ?? null) !== null) doc.updateBlock(id, { height: null });
|
|
13014
|
+
} else if (committed !== (block.attrs.height ?? null)) doc.updateBlock(id, { height: committed });
|
|
13015
|
+
};
|
|
13016
|
+
const onResizeKeyDown = (event) => {
|
|
13017
|
+
const wrapper = wrapperRef.current;
|
|
13018
|
+
if (wrapper === null) return;
|
|
13019
|
+
const min = readEmbedMinHeight(wrapper);
|
|
13020
|
+
const delta = event.key === "ArrowDown" ? EMBED_RESIZE_KEY_STEP : event.key === "ArrowUp" ? -16 : null;
|
|
13021
|
+
if (delta === null) return;
|
|
13022
|
+
event.preventDefault();
|
|
13023
|
+
const current = Math.max(heightRef.current ?? min, min);
|
|
13024
|
+
const next = Math.max(current + delta, min);
|
|
13025
|
+
setHeight(next <= min ? null : next);
|
|
13026
|
+
if (next <= min) {
|
|
13027
|
+
if ((block.attrs.height ?? null) !== null) doc.updateBlock(id, { height: null });
|
|
13028
|
+
} else if (next !== (block.attrs.height ?? null)) doc.updateBlock(id, { height: next });
|
|
13029
|
+
};
|
|
12882
13030
|
const onPointerDown = (event) => {
|
|
12883
13031
|
if (!panModifier || event.button !== 0) return;
|
|
12884
13032
|
if (event.target.closest("button") !== null) return;
|
|
@@ -12920,71 +13068,151 @@ function EmbedBlock({ id, block }) {
|
|
|
12920
13068
|
"data-block-id": id,
|
|
12921
13069
|
"data-kind": kind,
|
|
12922
13070
|
className: "\n group relative my-(--docblocks-block-spacing) h-(--docblocks-embed-height) overflow-hidden\n rounded-(--docblocks-radius) border-(length:--docblocks-divider-border-width)\n border-(--docblocks-divider-border-color) outline-none\n ",
|
|
12923
|
-
style: {
|
|
13071
|
+
style: {
|
|
13072
|
+
...height !== null ? { height: Math.max(height, tokenMin) } : {},
|
|
13073
|
+
cursor: panModifier ? panSession.current !== null ? "grabbing" : "grab" : void 0
|
|
13074
|
+
},
|
|
12924
13075
|
onPointerDown,
|
|
12925
13076
|
onPointerMove,
|
|
12926
13077
|
onPointerUp: endPan,
|
|
12927
13078
|
onPointerCancel: endPan,
|
|
12928
|
-
children: [
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
|
|
12951
|
-
|
|
12952
|
-
|
|
12953
|
-
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
12960
|
-
|
|
12961
|
-
|
|
12962
|
-
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
12980
|
-
|
|
12981
|
-
|
|
12982
|
-
|
|
12983
|
-
|
|
12984
|
-
|
|
12985
|
-
|
|
12986
|
-
|
|
12987
|
-
|
|
13079
|
+
children: [
|
|
13080
|
+
isSafeEmbedSrc(kind, src) ? /* @__PURE__ */ jsx("iframe", {
|
|
13081
|
+
"data-slot": "docblocks/embed",
|
|
13082
|
+
src,
|
|
13083
|
+
title: title ?? kind,
|
|
13084
|
+
loading: "lazy",
|
|
13085
|
+
sandbox: sandbox ?? void 0,
|
|
13086
|
+
className: "block h-full w-full border-0",
|
|
13087
|
+
style: {
|
|
13088
|
+
transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})`,
|
|
13089
|
+
transformOrigin: "0 0",
|
|
13090
|
+
pointerEvents: panModifier ? "none" : "auto"
|
|
13091
|
+
}
|
|
13092
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
13093
|
+
"data-slot": "docblocks/embed-blocked",
|
|
13094
|
+
className: "\n flex h-full w-full items-center justify-center p-4 text-center text-sm text-(--docblocks-muted-text-color)\n ",
|
|
13095
|
+
children: title ?? kind
|
|
13096
|
+
}),
|
|
13097
|
+
/* @__PURE__ */ jsxs("div", {
|
|
13098
|
+
"data-slot": "docblocks/embed-toolbar",
|
|
13099
|
+
className: "\n pointer-events-none absolute top-1.5 right-1.5 z-10 flex items-center gap-0.5 rounded-(--docblocks-radius)\n border-(length:--docblocks-divider-border-width) border-(--docblocks-divider-border-color)\n bg-(--docblocks-page-card-background) p-0.5 text-(--docblocks-text-color) opacity-0\n shadow-(--docblocks-formatbar-shadow) transition-opacity\n group-focus-within:pointer-events-auto group-focus-within:opacity-100\n group-hover:pointer-events-auto group-hover:opacity-100\n ",
|
|
13100
|
+
children: [
|
|
13101
|
+
/* @__PURE__ */ jsx(Button, {
|
|
13102
|
+
"data-slot": "docblocks/embed-zoom-out",
|
|
13103
|
+
variant: "ghost",
|
|
13104
|
+
size: "icon",
|
|
13105
|
+
"aria-label": t("embed.zoomOut"),
|
|
13106
|
+
className: "size-6 cursor-default rounded-sm text-(--docblocks-text-color) disabled:opacity-50",
|
|
13107
|
+
disabled: view.scale <= EMBED_MIN_SCALE,
|
|
13108
|
+
onMouseDown: preventDefault,
|
|
13109
|
+
onClick: zoomOut,
|
|
13110
|
+
children: /* @__PURE__ */ jsx(Minus, { className: "size-4" })
|
|
13111
|
+
}),
|
|
13112
|
+
/* @__PURE__ */ jsxs("span", {
|
|
13113
|
+
"data-slot": "docblocks/embed-zoom",
|
|
13114
|
+
className: "min-w-10 px-1 text-center text-xs text-(--docblocks-text-color) tabular-nums",
|
|
13115
|
+
children: [Math.round(view.scale * 100), "%"]
|
|
13116
|
+
}),
|
|
13117
|
+
/* @__PURE__ */ jsx(Button, {
|
|
13118
|
+
"data-slot": "docblocks/embed-zoom-in",
|
|
13119
|
+
variant: "ghost",
|
|
13120
|
+
size: "icon",
|
|
13121
|
+
"aria-label": t("embed.zoomIn"),
|
|
13122
|
+
className: "size-6 cursor-default rounded-sm text-(--docblocks-text-color) disabled:opacity-50",
|
|
13123
|
+
disabled: view.scale >= EMBED_MAX_SCALE,
|
|
13124
|
+
onMouseDown: preventDefault,
|
|
13125
|
+
onClick: zoomIn,
|
|
13126
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "size-4" })
|
|
13127
|
+
}),
|
|
13128
|
+
/* @__PURE__ */ jsx(Button, {
|
|
13129
|
+
"data-slot": "docblocks/embed-zoom-reset",
|
|
13130
|
+
variant: "ghost",
|
|
13131
|
+
size: "icon",
|
|
13132
|
+
"aria-label": t("embed.zoomReset"),
|
|
13133
|
+
className: "size-6 cursor-default rounded-sm text-(--docblocks-text-color) disabled:opacity-50",
|
|
13134
|
+
disabled: atIdentity,
|
|
13135
|
+
onMouseDown: preventDefault,
|
|
13136
|
+
onClick: resetView,
|
|
13137
|
+
children: /* @__PURE__ */ jsx(RotateCcw, { className: "size-4" })
|
|
13138
|
+
}),
|
|
13139
|
+
!viewMode && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
13140
|
+
className: "mx-0.5 h-4 w-px bg-(--docblocks-divider-border-color)",
|
|
13141
|
+
"aria-hidden": true
|
|
13142
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
13143
|
+
"data-slot": "docblocks/embed-edit-url",
|
|
13144
|
+
variant: "ghost",
|
|
13145
|
+
size: "icon",
|
|
13146
|
+
"aria-label": t("embed.editUrl"),
|
|
13147
|
+
className: "size-6 cursor-default rounded-sm text-(--docblocks-text-color)",
|
|
13148
|
+
onMouseDown: preventDefault,
|
|
13149
|
+
onClick: openEdit,
|
|
13150
|
+
children: /* @__PURE__ */ jsx(Pen, { className: "size-4" })
|
|
13151
|
+
})] })
|
|
13152
|
+
]
|
|
13153
|
+
}),
|
|
13154
|
+
!viewMode && editOpen && /* @__PURE__ */ jsxs("div", {
|
|
13155
|
+
"data-slot": "docblocks/embed-edit",
|
|
13156
|
+
className: "\n absolute top-8 right-1.5 z-10 flex w-56 flex-col gap-1.5 rounded-(--docblocks-radius)\n border-(length:--docblocks-divider-border-width) border-(--docblocks-divider-border-color)\n bg-(--docblocks-page-card-background) p-1.5 text-(--docblocks-text-color)\n shadow-(--docblocks-formatbar-shadow)\n ",
|
|
13157
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
13158
|
+
className: "flex items-center gap-1",
|
|
13159
|
+
children: [
|
|
13160
|
+
/* @__PURE__ */ jsx(Input, {
|
|
13161
|
+
size: "sm",
|
|
13162
|
+
ref: editInputRef,
|
|
13163
|
+
type: "text",
|
|
13164
|
+
"data-slot": "docblocks/embed-edit-input",
|
|
13165
|
+
className: "h-7 flex-1 rounded-sm",
|
|
13166
|
+
placeholder: t("embed.srcPlaceholder"),
|
|
13167
|
+
value: draft,
|
|
13168
|
+
onChange: (event) => setDraft(event.target.value),
|
|
13169
|
+
onKeyDown: onEditKeyDown
|
|
13170
|
+
}),
|
|
13171
|
+
/* @__PURE__ */ jsx(Button, {
|
|
13172
|
+
"data-slot": "docblocks/embed-edit-save",
|
|
13173
|
+
variant: "ghost",
|
|
13174
|
+
size: "icon",
|
|
13175
|
+
"aria-label": t("embed.saveUrl"),
|
|
13176
|
+
className: "size-7 shrink-0 cursor-default rounded-sm text-(--docblocks-text-color) disabled:opacity-50",
|
|
13177
|
+
disabled: !canSave,
|
|
13178
|
+
onMouseDown: preventDefault,
|
|
13179
|
+
onClick: commitEdit,
|
|
13180
|
+
children: /* @__PURE__ */ jsx(Check, { className: "size-4" })
|
|
13181
|
+
}),
|
|
13182
|
+
/* @__PURE__ */ jsx(Button, {
|
|
13183
|
+
"data-slot": "docblocks/embed-edit-cancel",
|
|
13184
|
+
variant: "ghost",
|
|
13185
|
+
size: "icon",
|
|
13186
|
+
"aria-label": t("embed.cancelEdit"),
|
|
13187
|
+
className: "size-7 shrink-0 cursor-default rounded-sm text-(--docblocks-text-color)",
|
|
13188
|
+
onMouseDown: preventDefault,
|
|
13189
|
+
onClick: closeEdit,
|
|
13190
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
13191
|
+
})
|
|
13192
|
+
]
|
|
13193
|
+
}), editHint !== null && /* @__PURE__ */ jsx("p", {
|
|
13194
|
+
"data-slot": "docblocks/embed-edit-hint",
|
|
13195
|
+
className: "px-1 text-xs text-destructive",
|
|
13196
|
+
children: editHint
|
|
13197
|
+
})]
|
|
13198
|
+
}),
|
|
13199
|
+
!viewMode && /* @__PURE__ */ jsx("div", {
|
|
13200
|
+
"data-slot": "docblocks/embed-resize",
|
|
13201
|
+
role: "slider",
|
|
13202
|
+
"aria-orientation": "vertical",
|
|
13203
|
+
"aria-valuemin": tokenMin,
|
|
13204
|
+
"aria-valuenow": Math.max(height ?? tokenMin, tokenMin),
|
|
13205
|
+
"aria-label": t("embed.resizeHeight"),
|
|
13206
|
+
tabIndex: 0,
|
|
13207
|
+
className: "\n absolute inset-x-0 bottom-0 z-10 flex h-2 cursor-row-resize items-end justify-center opacity-0\n transition-opacity outline-none\n group-focus-within:opacity-100\n group-hover:opacity-100\n ",
|
|
13208
|
+
onPointerDown: onResizePointerDown,
|
|
13209
|
+
onPointerMove: onResizePointerMove,
|
|
13210
|
+
onPointerUp: endResize,
|
|
13211
|
+
onPointerCancel: endResize,
|
|
13212
|
+
onKeyDown: onResizeKeyDown,
|
|
13213
|
+
children: /* @__PURE__ */ jsx("div", { className: "mb-0.5 h-0.5 w-10 rounded-full bg-(--docblocks-divider-border-color)" })
|
|
13214
|
+
})
|
|
13215
|
+
]
|
|
12988
13216
|
});
|
|
12989
13217
|
}
|
|
12990
13218
|
//#endregion
|
|
@@ -18092,7 +18320,8 @@ function TextBlock({ id, block, tag, nested, collapsible = false, flat = false,
|
|
|
18092
18320
|
size: "icon",
|
|
18093
18321
|
onClick: (event) => deleteBlock(event.currentTarget, id),
|
|
18094
18322
|
onMouseDown: onControlMouseDown,
|
|
18095
|
-
className: cn("absolute top-[calc((var(--docblocks-line-box-height)-var(--docblocks-gutter-size))/2)]
|
|
18323
|
+
className: cn("absolute top-[calc((var(--docblocks-line-box-height)-var(--docblocks-gutter-size))/2)]", "cursor-pointer rounded-full opacity-0 group-hover:opacity-100", "text-(--docblocks-gutter-color)", "size-5", selected && "opacity-100"),
|
|
18324
|
+
style: { right: "var(--docblocks-gutter-left)" },
|
|
18096
18325
|
children: /* @__PURE__ */ jsx(Trash, {})
|
|
18097
18326
|
}),
|
|
18098
18327
|
block.type === "list-item" && block.attrs.list === "task" && /* @__PURE__ */ jsx(Checkbox, {
|
|
@@ -18660,67 +18889,36 @@ function TitleMenu({ slot, ariaLabel, open, onOpenChange, onCommit }) {
|
|
|
18660
18889
|
//#endregion
|
|
18661
18890
|
//#region src/canvas/FrameBlock.tsx
|
|
18662
18891
|
/**
|
|
18663
|
-
* The canvas frame's renderer: the
|
|
18664
|
-
* frame
|
|
18665
|
-
* geometry + the selection + the move/resize preview;
|
|
18666
|
-
*
|
|
18667
|
-
*
|
|
18668
|
-
*
|
|
18669
|
-
* the
|
|
18670
|
-
*
|
|
18671
|
-
*
|
|
18672
|
-
*
|
|
18673
|
-
*
|
|
18674
|
-
|
|
18675
|
-
|
|
18676
|
-
* chip (the container's idiom) stands over the shared CanvasTitleChip.
|
|
18677
|
-
* The frame-chain's cycle guard: a frame rendered inside another frame's
|
|
18678
|
-
* window carries the chain's ids and excludes the chain + itself from its
|
|
18679
|
-
* window's content (the chain grows strictly down the nesting — the
|
|
18680
|
-
* recursion is finite, no depth limit needed).
|
|
18681
|
-
*/
|
|
18682
|
-
/** The frame chain's stand of a direct surface child (no window above).
|
|
18683
|
-
* The stable module constant stands the default — the memo's identity
|
|
18684
|
-
* comparison bails over the resting items. */
|
|
18685
|
-
const EMPTY_FRAME_CHAIN = /* @__PURE__ */ new Set();
|
|
18686
|
-
function FrameBlock({ id, movePreview = null, resizeBox = null, selected = false, frameChain = EMPTY_FRAME_CHAIN }) {
|
|
18892
|
+
* The canvas frame's renderer: the frame's boundary — the rectangle that
|
|
18893
|
+
* defines the region the page-frame block renders. The CanvasItem's wrapper
|
|
18894
|
+
* carries the geometry + the selection + the move/resize preview; the box
|
|
18895
|
+
* stands the container's thin rounded border (the selection's color on the
|
|
18896
|
+
* selection) over the canvas's own background. The frame renders the
|
|
18897
|
+
* boundary only — no content copy: the canvas's own figures stand behind the
|
|
18898
|
+
* transparent box (the canvas frame is a region marker, not a window), and
|
|
18899
|
+
* the content's mirror (the surface's children clipped to the frame's
|
|
18900
|
+
* region) stands on the page-frame block's side (FramePageBlock). The
|
|
18901
|
+
* frame's title chip (the container's idiom) stands over the shared
|
|
18902
|
+
* CanvasTitleChip.
|
|
18903
|
+
*/
|
|
18904
|
+
function FrameBlock({ id, selected = false }) {
|
|
18687
18905
|
const { t } = useTranslation("canvas");
|
|
18688
|
-
const {
|
|
18906
|
+
const { parentType } = useBlockContext();
|
|
18689
18907
|
const block = useBlock(id);
|
|
18690
18908
|
if (parentType !== "surface" || block === void 0 || block.type !== "frame") return /* @__PURE__ */ jsx(PlaceholderBlock, { type: "frame" });
|
|
18691
|
-
const committed = effectiveGeometry(block);
|
|
18692
|
-
const effective = {
|
|
18693
|
-
x: (resizeBox?.x ?? committed.x) + (movePreview?.dx ?? 0),
|
|
18694
|
-
y: (resizeBox?.y ?? committed.y) + (movePreview?.dy ?? 0),
|
|
18695
|
-
w: resizeBox?.w ?? committed.w,
|
|
18696
|
-
h: resizeBox?.h ?? committed.h
|
|
18697
|
-
};
|
|
18698
|
-
const offset = frameWindowOffset(effective);
|
|
18699
18909
|
const selectedColor = "var(--docblocks-canvas-selection-border-color)";
|
|
18700
|
-
const ringColor = selected ? selectedColor : "var(--docblocks-canvas-group-border-color)";
|
|
18701
|
-
const boxStyle = {
|
|
18702
|
-
backgroundColor: "var(--docblocks-canvas-background)",
|
|
18703
|
-
...frameDotBackground(effective.x - effective.w / 2, effective.y - effective.h / 2),
|
|
18704
|
-
boxShadow: `inset 0 0 0 1px ${ringColor}`,
|
|
18705
|
-
outline: selected ? `var(--docblocks-canvas-selection-border-width) solid ${selectedColor}` : void 0,
|
|
18706
|
-
outlineOffset: 1
|
|
18707
|
-
};
|
|
18708
|
-
const excludedIds = useMemo(() => [...frameChain, id], [frameChain, id]);
|
|
18709
|
-
const childFrameChain = useMemo(() => /* @__PURE__ */ new Set([...frameChain, id]), [frameChain, id]);
|
|
18710
18910
|
return /* @__PURE__ */ jsxs("div", {
|
|
18711
18911
|
"data-slot": "docblocks/frame",
|
|
18712
18912
|
"data-block-id": id,
|
|
18713
18913
|
className: "h-full w-full",
|
|
18714
18914
|
children: [/* @__PURE__ */ jsx("div", {
|
|
18715
|
-
"data-slot": "docblocks/frame-
|
|
18716
|
-
className: "absolute inset-0
|
|
18717
|
-
style:
|
|
18718
|
-
|
|
18719
|
-
|
|
18720
|
-
|
|
18721
|
-
|
|
18722
|
-
frameChain: childFrameChain
|
|
18723
|
-
})
|
|
18915
|
+
"data-slot": "docblocks/frame-boundary",
|
|
18916
|
+
className: "absolute inset-0 rounded-(--docblocks-radius)",
|
|
18917
|
+
style: {
|
|
18918
|
+
boxShadow: `inset 0 0 0 1px ${selected ? selectedColor : "var(--docblocks-canvas-group-border-color)"}`,
|
|
18919
|
+
outline: selected ? `var(--docblocks-canvas-selection-border-width) solid ${selectedColor}` : void 0,
|
|
18920
|
+
outlineOffset: 1
|
|
18921
|
+
}
|
|
18724
18922
|
}), /* @__PURE__ */ jsx(CanvasTitleChip, {
|
|
18725
18923
|
id,
|
|
18726
18924
|
title: block.attrs?.title ?? "",
|
|
@@ -22768,7 +22966,7 @@ const CANVAS_ITEM_LABEL_KEYS = {
|
|
|
22768
22966
|
* order, the line can stand over or under a figure); `interactive`
|
|
22769
22967
|
* toggles the line's hit path (the page view's surface stands it inert).
|
|
22770
22968
|
*/
|
|
22771
|
-
const CanvasItem = memo(function CanvasItem({ id, pendingConnector = null, movePreview = null, resizeBox = null, interactive = true, kbFocused = false
|
|
22969
|
+
const CanvasItem = memo(function CanvasItem({ id, pendingConnector = null, movePreview = null, resizeBox = null, interactive = true, kbFocused = false }) {
|
|
22772
22970
|
const { t } = useTranslation("canvas");
|
|
22773
22971
|
const { store, renderers } = useEditorContext();
|
|
22774
22972
|
const { siblings } = useBlockContext();
|
|
@@ -22803,10 +23001,7 @@ const CanvasItem = memo(function CanvasItem({ id, pendingConnector = null, moveP
|
|
|
22803
23001
|
tag: "div"
|
|
22804
23002
|
}) : block.type === "frame" ? /* @__PURE__ */ jsx(FrameBlock, {
|
|
22805
23003
|
id,
|
|
22806
|
-
|
|
22807
|
-
resizeBox,
|
|
22808
|
-
selected,
|
|
22809
|
-
frameChain
|
|
23004
|
+
selected
|
|
22810
23005
|
}) : block.type === "group" ? /* @__PURE__ */ jsx(GroupBlock, {
|
|
22811
23006
|
id,
|
|
22812
23007
|
block,
|
|
@@ -22853,7 +23048,7 @@ const CanvasItem = memo(function CanvasItem({ id, pendingConnector = null, moveP
|
|
|
22853
23048
|
style,
|
|
22854
23049
|
children: content
|
|
22855
23050
|
});
|
|
22856
|
-
}, (prev, next) => prev.id === next.id && prev.pendingConnector === next.pendingConnector && prev.movePreview === next.movePreview && prev.resizeBox === next.resizeBox && prev.interactive === next.interactive && prev.kbFocused === next.kbFocused
|
|
23051
|
+
}, (prev, next) => prev.id === next.id && prev.pendingConnector === next.pendingConnector && prev.movePreview === next.movePreview && prev.resizeBox === next.resizeBox && prev.interactive === next.interactive && prev.kbFocused === next.kbFocused);
|
|
22857
23052
|
//#endregion
|
|
22858
23053
|
//#region src/canvas/ConnectorArrowDefs.tsx
|
|
22859
23054
|
/** The surface's arrowhead defs (the hidden 0x0 svg): the marker ids stand
|
|
@@ -22968,7 +23163,7 @@ function ConnectorArrowDefs({ id }) {
|
|
|
22968
23163
|
* context providers are not DOM nodes, so several surfaces merge onto the
|
|
22969
23164
|
* same canvas in root order (the later surface paints on top: the DOM
|
|
22970
23165
|
* order is the z-order). */
|
|
22971
|
-
function SurfaceLayer({ id, children, pendingConnector, movePreview, resizePreview, endpointDrag, endpointPreview, showPorts, endpointSnap, kbFocusId = null, inert = false, excludedIds
|
|
23166
|
+
function SurfaceLayer({ id, children, pendingConnector, movePreview, resizePreview, endpointDrag, endpointPreview, showPorts, endpointSnap, kbFocusId = null, inert = false, excludedIds }) {
|
|
22972
23167
|
const contextValue = useBlockContextValue(id, children, "surface");
|
|
22973
23168
|
const session = useMemo(() => ({
|
|
22974
23169
|
movePreview,
|
|
@@ -23001,8 +23196,7 @@ function SurfaceLayer({ id, children, pendingConnector, movePreview, resizePrevi
|
|
|
23001
23196
|
pendingConnector,
|
|
23002
23197
|
movePreview: movePreview !== null && movePreview.ids.includes(childId) ? movePreview : null,
|
|
23003
23198
|
resizeBox: resizePreview !== null && resizePreview.id === childId ? resizePreview.box : null,
|
|
23004
|
-
kbFocused: kbFocusId === childId
|
|
23005
|
-
frameChain
|
|
23199
|
+
kbFocused: kbFocusId === childId
|
|
23006
23200
|
}, childId)),
|
|
23007
23201
|
showPorts && /* @__PURE__ */ jsx(PortDots, {
|
|
23008
23202
|
id,
|
|
@@ -23097,8 +23291,7 @@ function PortDots({ id, snapped }) {
|
|
|
23097
23291
|
//#endregion
|
|
23098
23292
|
//#region src/canvas/FrameWindowContent.tsx
|
|
23099
23293
|
const EMPTY_IDS$1 = [];
|
|
23100
|
-
|
|
23101
|
-
function FrameWindowContent({ surfaceId, offset, excludedIds = EMPTY_IDS$1, frameChain = EMPTY_CHAIN, className }) {
|
|
23294
|
+
function FrameWindowContent({ surfaceId, offset, excludedIds = EMPTY_IDS$1, className }) {
|
|
23102
23295
|
const surface = useBlock(surfaceId);
|
|
23103
23296
|
const children = surface !== void 0 && surface.type === "surface" ? surface.children ?? EMPTY_IDS$1 : EMPTY_IDS$1;
|
|
23104
23297
|
return /* @__PURE__ */ jsx("div", {
|
|
@@ -23124,8 +23317,7 @@ function FrameWindowContent({ surfaceId, offset, excludedIds = EMPTY_IDS$1, fram
|
|
|
23124
23317
|
endpointSnap: null,
|
|
23125
23318
|
kbFocusId: null,
|
|
23126
23319
|
inert: true,
|
|
23127
|
-
excludedIds
|
|
23128
|
-
frameChain
|
|
23320
|
+
excludedIds
|
|
23129
23321
|
}), /* @__PURE__ */ jsx(ConnectorLabelChips, { children })]
|
|
23130
23322
|
})
|
|
23131
23323
|
});
|
|
@@ -23134,11 +23326,13 @@ function FrameWindowContent({ surfaceId, offset, excludedIds = EMPTY_IDS$1, fram
|
|
|
23134
23326
|
//#region src/blocks/FramePageBlock.tsx
|
|
23135
23327
|
/**
|
|
23136
23328
|
* The page-frame block's renderer: the inline window into the referenced
|
|
23137
|
-
* canvas frame's surface — the
|
|
23138
|
-
*
|
|
23139
|
-
*
|
|
23140
|
-
*
|
|
23141
|
-
* 1:1 (no fit/zoom controls — the v1 boundary). The
|
|
23329
|
+
* canvas frame's surface — the content's mirror (FrameWindowContent over
|
|
23330
|
+
* the window's offset; the box's background the canvas's own background +
|
|
23331
|
+
* the dot grid phase-aligned to the world's grid; the container's thin
|
|
23332
|
+
* rounded border), inlined in the page flow at
|
|
23333
|
+
* 1:1 (no fit/zoom controls — the v1 boundary). The canvas frame stands the
|
|
23334
|
+
* boundary only (a region marker) — this page block is the sole renderer
|
|
23335
|
+
* of the frame's content. The box's height is the
|
|
23142
23336
|
* referenced frame's 1:1; the width is the frame's width clipped to the
|
|
23143
23337
|
* row's measured width (the window's center stays at the frame's center —
|
|
23144
23338
|
* a narrow row clips the wide window horizontally, the offset and the dot
|
|
@@ -23232,7 +23426,6 @@ function FramePageWindow({ id, frameId, surfaceId, referenced }) {
|
|
|
23232
23426
|
};
|
|
23233
23427
|
const title = referenced.attrs?.title ?? "";
|
|
23234
23428
|
const excludedIds = useMemo(() => [frameId], [frameId]);
|
|
23235
|
-
const frameChain = useMemo(() => /* @__PURE__ */ new Set([frameId]), [frameId]);
|
|
23236
23429
|
return /* @__PURE__ */ jsx("div", {
|
|
23237
23430
|
ref: rowRef,
|
|
23238
23431
|
"data-slot": "docblocks/frame-page",
|
|
@@ -23250,8 +23443,7 @@ function FramePageWindow({ id, frameId, surfaceId, referenced }) {
|
|
|
23250
23443
|
children: /* @__PURE__ */ jsx(FrameWindowContent, {
|
|
23251
23444
|
surfaceId,
|
|
23252
23445
|
offset,
|
|
23253
|
-
excludedIds
|
|
23254
|
-
frameChain
|
|
23446
|
+
excludedIds
|
|
23255
23447
|
})
|
|
23256
23448
|
})
|
|
23257
23449
|
});
|
|
@@ -27766,6 +27958,15 @@ function positionAtPoint(doc, root, x, y) {
|
|
|
27766
27958
|
* soft-break lines) itself — a permanent capture listener, independent of
|
|
27767
27959
|
* the session (the real pointer's third press starts no session: the
|
|
27768
27960
|
* pointer's detail stands 3 and the session guard returns).
|
|
27961
|
+
*
|
|
27962
|
+
* The iframe's focus exception: a press while the document's focus stands
|
|
27963
|
+
* in a frame (the embed's iframe) tears the session down and lets the
|
|
27964
|
+
* mousedown's default stand — the default's focus move out of the frame
|
|
27965
|
+
* is the editor's document's focus, and suppressing it would seat the
|
|
27966
|
+
* caret over the unfocused document (no visible caret, the keys' no-
|
|
27967
|
+
* stand). The browser's native caret's seat stands the press; a double
|
|
27968
|
+
* press over the focused frame stands the same stand over the detail's
|
|
27969
|
+
* check.
|
|
27769
27970
|
*/
|
|
27770
27971
|
function useCrossDrag(rootRef, doc) {
|
|
27771
27972
|
const sessionRef = useRef(null);
|
|
@@ -27799,12 +28000,19 @@ function useCrossDrag(rootRef, doc) {
|
|
|
27799
28000
|
if (session === null || root === null || event.button !== 0) return;
|
|
27800
28001
|
const target = event.target instanceof Element ? event.target : null;
|
|
27801
28002
|
if (target === null || !root.contains(target)) return;
|
|
27802
|
-
|
|
28003
|
+
const tearDown = () => {
|
|
27803
28004
|
sessionRef.current = null;
|
|
27804
28005
|
window.removeEventListener("pointermove", move);
|
|
27805
28006
|
window.removeEventListener("pointerup", end);
|
|
27806
28007
|
window.removeEventListener("pointercancel", end);
|
|
27807
28008
|
window.removeEventListener("mousedown", mousedown, true);
|
|
28009
|
+
};
|
|
28010
|
+
if (event.detail >= 2) {
|
|
28011
|
+
tearDown();
|
|
28012
|
+
return;
|
|
28013
|
+
}
|
|
28014
|
+
if (document.activeElement instanceof HTMLIFrameElement) {
|
|
28015
|
+
tearDown();
|
|
27808
28016
|
return;
|
|
27809
28017
|
}
|
|
27810
28018
|
event.preventDefault();
|
|
@@ -27846,6 +28054,7 @@ function useCrossDrag(rootRef, doc) {
|
|
|
27846
28054
|
if (target === null || !root.contains(target)) return;
|
|
27847
28055
|
const inner = target.closest("[data-slot=\"docblocks/block-content\"]");
|
|
27848
28056
|
if (inner === null || inner.closest("[data-slot=\"docblocks/editor\"]") !== root) return;
|
|
28057
|
+
if (document.activeElement instanceof HTMLIFrameElement) return;
|
|
27849
28058
|
const blockId = inner.dataset.blockId;
|
|
27850
28059
|
const head = locatePosition(inner, 0);
|
|
27851
28060
|
const tail = locatePosition(inner, doc.getTextLength(blockId));
|
|
@@ -28357,7 +28566,8 @@ const RowItem = memo(function RowItem({ node, layoutSlide, isDragged, animated,
|
|
|
28357
28566
|
size: "icon",
|
|
28358
28567
|
onClick: (event) => deleteBlock(event.currentTarget, node.id),
|
|
28359
28568
|
onMouseDown: onHandleMouseDown,
|
|
28360
|
-
className: cn("absolute top-[calc((var(--docblocks-line-box-height)-var(--docblocks-gutter-size))/2)]
|
|
28569
|
+
className: cn("absolute top-[calc((var(--docblocks-line-box-height)-var(--docblocks-gutter-size))/2)]", "cursor-pointer rounded-full opacity-0 group-hover:opacity-100", "text-(--docblocks-gutter-color)", "size-5", selected && "opacity-100"),
|
|
28570
|
+
style: { right: "var(--docblocks-gutter-left)" },
|
|
28361
28571
|
children: /* @__PURE__ */ jsx(Trash, {})
|
|
28362
28572
|
}),
|
|
28363
28573
|
/* @__PURE__ */ jsx(BlockNodeView, {
|