@noirmd/previewer 1.1.0 → 1.2.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/dist/NReditor.cjs CHANGED
@@ -347,6 +347,62 @@ function useDebounce(value, delay) {
347
347
  return debouncedValue;
348
348
  }
349
349
 
350
+ // react/useTailwindCDN.ts
351
+ var import_react2 = require("react");
352
+ var CDN_URL = "https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4";
353
+ var SCRIPT_ID = "tailwind-cdn-v4-runtime";
354
+ var CONFIG_ID = "tailwind-cdn-v4-config";
355
+ var TAILWIND_CDN_CONFIG = `
356
+ window.tailwind = window.tailwind || {};
357
+ window.tailwind.config = {
358
+ corePlugins: {
359
+ preflight: false,
360
+ },
361
+ };
362
+ `;
363
+ function useLazyTailwindCDN(enabled) {
364
+ const loadedRef = (0, import_react2.useRef)(false);
365
+ (0, import_react2.useEffect)(() => {
366
+ if (!enabled) {
367
+ window.__twCDNRefs = (window.__twCDNRefs ?? 1) - 1;
368
+ if ((window.__twCDNRefs ?? 0) <= 0) {
369
+ document.getElementById(SCRIPT_ID)?.remove();
370
+ document.getElementById(CONFIG_ID)?.remove();
371
+ window.__twCDNRefs = 0;
372
+ loadedRef.current = false;
373
+ }
374
+ return;
375
+ }
376
+ window.__twCDNRefs = (window.__twCDNRefs ?? 0) + 1;
377
+ if (!document.getElementById(CONFIG_ID)) {
378
+ const configScript = document.createElement("script");
379
+ configScript.id = CONFIG_ID;
380
+ configScript.textContent = TAILWIND_CDN_CONFIG;
381
+ document.head.appendChild(configScript);
382
+ }
383
+ if (!document.getElementById(SCRIPT_ID)) {
384
+ const script = document.createElement("script");
385
+ script.id = SCRIPT_ID;
386
+ script.src = CDN_URL;
387
+ document.head.appendChild(script);
388
+ loadedRef.current = true;
389
+ }
390
+ return () => {
391
+ window.__twCDNRefs = (window.__twCDNRefs ?? 1) - 1;
392
+ if ((window.__twCDNRefs ?? 0) <= 0) {
393
+ document.getElementById(SCRIPT_ID)?.remove();
394
+ document.getElementById(CONFIG_ID)?.remove();
395
+ window.__twCDNRefs = 0;
396
+ loadedRef.current = false;
397
+ }
398
+ };
399
+ }, [enabled]);
400
+ }
401
+ function scanTailwindCDN() {
402
+ const tw = window.tailwind;
403
+ if (tw?.scan) tw.scan();
404
+ }
405
+
350
406
  // react/CustomMarkdownRenderer.tsx
351
407
  var import_react10 = __toESM(require("react"), 1);
352
408
 
@@ -442,7 +498,7 @@ function parseHtmlAttrs(attrsString) {
442
498
  // core/parser.ts
443
499
  function parseMarkdown(markdown2) {
444
500
  if (!markdown2) return [];
445
- const lines = markdown2.split("\n");
501
+ const lines = markdown2.replace(/\r\n/g, "\n").replace(/\r/g, "").split("\n");
446
502
  const result = [];
447
503
  let i = 0;
448
504
  while (i < lines.length) {
@@ -769,7 +825,7 @@ function splitSlots(rawContent) {
769
825
  }
770
826
 
771
827
  // react/ui-components.tsx
772
- var import_react2 = require("react");
828
+ var import_react3 = require("react");
773
829
 
774
830
  // react/highlightSetup.ts
775
831
  var import_core = __toESM(require("highlight.js/lib/core"), 1);
@@ -861,8 +917,8 @@ var IconRenderer = ({ iconName, extraClasses = "" }) => {
861
917
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: baseClass, "aria-hidden": "true", children: iconName });
862
918
  };
863
919
  var CodeBlock = ({ code, language, title }) => {
864
- const [copied, setCopied] = (0, import_react2.useState)(false);
865
- const timerRef = (0, import_react2.useRef)(null);
920
+ const [copied, setCopied] = (0, import_react3.useState)(false);
921
+ const timerRef = (0, import_react3.useRef)(null);
866
922
  const lang = language?.split(/[\s{]/)[0]?.trim() || "";
867
923
  let html;
868
924
  try {
@@ -928,7 +984,7 @@ var Details = ({
928
984
  style,
929
985
  children
930
986
  }) => {
931
- const [isOpen, setIsOpen] = (0, import_react2.useState)(defaultOpen);
987
+ const [isOpen, setIsOpen] = (0, import_react3.useState)(defaultOpen);
932
988
  const iconToRender = icon || "play_arrow";
933
989
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
934
990
  "details",
@@ -1130,7 +1186,7 @@ var AdmonitionDirective = ({
1130
1186
  var AdmonitionDirective_default = AdmonitionDirective;
1131
1187
 
1132
1188
  // react/directives/CardDirective.tsx
1133
- var import_react3 = __toESM(require("react"), 1);
1189
+ var import_react4 = __toESM(require("react"), 1);
1134
1190
  var import_jsx_runtime4 = require("react/jsx-runtime");
1135
1191
  var CardDirective = ({
1136
1192
  directiveType,
@@ -1140,7 +1196,7 @@ var CardDirective = ({
1140
1196
  context,
1141
1197
  options = {}
1142
1198
  }) => {
1143
- const stableId = (0, import_react3.useId)();
1199
+ const stableId = (0, import_react4.useId)();
1144
1200
  const { title, image, icon, class: customClass, url, target } = props;
1145
1201
  const hasDescription = !!slots.description;
1146
1202
  const { isSingleCard } = options;
@@ -1151,7 +1207,7 @@ var CardDirective = ({
1151
1207
  const inlineStyles = props.style ? parseCssString(props.style) : {};
1152
1208
  const description = hasDescription ? renderSlot("description") : null;
1153
1209
  const content = renderSlot("content") || renderSlot("default");
1154
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react3.default.Fragment, { children: [
1210
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react4.default.Fragment, { children: [
1155
1211
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1156
1212
  "div",
1157
1213
  {
@@ -1236,14 +1292,14 @@ var DetailsDirective = ({
1236
1292
  var DetailsDirective_default = DetailsDirective;
1237
1293
 
1238
1294
  // react/directives/ModalDirective.tsx
1239
- var import_react4 = require("react");
1295
+ var import_react5 = require("react");
1240
1296
  var import_jsx_runtime6 = require("react/jsx-runtime");
1241
1297
  var ModalDirective = ({
1242
1298
  props,
1243
1299
  renderSlot,
1244
1300
  context
1245
1301
  }) => {
1246
- const stableId = (0, import_react4.useId)();
1302
+ const stableId = (0, import_react5.useId)();
1247
1303
  const modalId = `modal-${props.id || stableId}`;
1248
1304
  const label = props.label || props.title || "Open";
1249
1305
  const modalTitle = props.title || "Modal";
@@ -1287,7 +1343,7 @@ var ModalDirective = ({
1287
1343
  var ModalDirective_default = ModalDirective;
1288
1344
 
1289
1345
  // react/directives/ButtonDirective.tsx
1290
- var import_react5 = __toESM(require("react"), 1);
1346
+ var import_react6 = __toESM(require("react"), 1);
1291
1347
  var import_jsx_runtime7 = require("react/jsx-runtime");
1292
1348
  var ButtonDirective = ({
1293
1349
  props,
@@ -1330,13 +1386,13 @@ var ButtonDirective = ({
1330
1386
  const slotContent = renderSlot("default");
1331
1387
  const findLinks = (element) => {
1332
1388
  if (!element) return [];
1333
- if (import_react5.default.isValidElement(element) && element.type === "a") {
1389
+ if (import_react6.default.isValidElement(element) && element.type === "a") {
1334
1390
  return [element];
1335
1391
  }
1336
1392
  if (Array.isArray(element)) {
1337
1393
  return element.flatMap(findLinks);
1338
1394
  }
1339
- if (import_react5.default.isValidElement(element) && element.props.children) {
1395
+ if (import_react6.default.isValidElement(element) && element.props.children) {
1340
1396
  return findLinks(element.props.children);
1341
1397
  }
1342
1398
  return [];
@@ -1344,7 +1400,7 @@ var ButtonDirective = ({
1344
1400
  const links = findLinks(slotContent);
1345
1401
  if (links.length > 0) {
1346
1402
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `not-prose ${wrapperClass}`.trim(), children: links.map(
1347
- (link, index) => import_react5.default.cloneElement(
1403
+ (link, index) => import_react6.default.cloneElement(
1348
1404
  link,
1349
1405
  {
1350
1406
  key: index,
@@ -1389,7 +1445,7 @@ var WrapperDirective = ({
1389
1445
  var WrapperDirective_default = WrapperDirective;
1390
1446
 
1391
1447
  // react/directives/SlideDirective.tsx
1392
- var import_react6 = require("react");
1448
+ var import_react7 = require("react");
1393
1449
  var import_jsx_runtime9 = require("react/jsx-runtime");
1394
1450
  var slideCounter = 0;
1395
1451
  var SlideDirective = ({
@@ -1400,23 +1456,23 @@ var SlideDirective = ({
1400
1456
  const rawContent = slots.default || "";
1401
1457
  const lines = rawContent.split("\n").map((l) => l.trim()).filter(Boolean);
1402
1458
  const elements = lines.map((line) => context.parseMarkdown(line));
1403
- const [current, setCurrent] = (0, import_react6.useState)(0);
1459
+ const [current, setCurrent] = (0, import_react7.useState)(0);
1404
1460
  const interval = parseInt(props.interval || "3000", 10);
1405
1461
  const speed = parseInt(props.speed || "500", 10);
1406
- const elRefs = (0, import_react6.useRef)([]);
1407
- const [maxH, setMaxH] = (0, import_react6.useState)(0);
1408
- (0, import_react6.useLayoutEffect)(() => {
1462
+ const elRefs = (0, import_react7.useRef)([]);
1463
+ const [maxH, setMaxH] = (0, import_react7.useState)(0);
1464
+ (0, import_react7.useLayoutEffect)(() => {
1409
1465
  let h = 0;
1410
1466
  elRefs.current.forEach((el) => {
1411
1467
  if (el) h = Math.max(h, el.offsetHeight);
1412
1468
  });
1413
1469
  if (h > 0) setMaxH(h);
1414
1470
  }, [elements]);
1415
- const cycle = (0, import_react6.useCallback)(() => {
1471
+ const cycle = (0, import_react7.useCallback)(() => {
1416
1472
  if (elements.length <= 1) return;
1417
1473
  setCurrent((prev) => (prev + 1) % elements.length);
1418
1474
  }, [elements.length]);
1419
- (0, import_react6.useEffect)(() => {
1475
+ (0, import_react7.useEffect)(() => {
1420
1476
  if (elements.length <= 1) return;
1421
1477
  const id = setInterval(cycle, interval);
1422
1478
  return () => clearInterval(id);
@@ -1540,15 +1596,6 @@ var DirectiveRenderer_default = DirectiveRenderer;
1540
1596
 
1541
1597
  // react/RawHtmlRenderer.tsx
1542
1598
  var import_react8 = require("react");
1543
-
1544
- // react/useTailwindCDN.ts
1545
- var import_react7 = require("react");
1546
- function scanTailwindCDN() {
1547
- const tw = window.tailwind;
1548
- if (tw?.scan) tw.scan();
1549
- }
1550
-
1551
- // react/RawHtmlRenderer.tsx
1552
1599
  var import_jsx_runtime11 = require("react/jsx-runtime");
1553
1600
  var RawHtmlRenderer = ({
1554
1601
  content,
@@ -2262,12 +2309,15 @@ var NReditor = ({
2262
2309
  onChange,
2263
2310
  className,
2264
2311
  debounceMs = 300,
2265
- tailwindCDN = false
2312
+ tailwindCDN = false,
2313
+ onGuide,
2314
+ onConfig
2266
2315
  }) => {
2267
2316
  const editorRef = (0, import_react11.useRef)(null);
2268
2317
  const [isAllFolded, setIsAllFolded] = (0, import_react11.useState)(false);
2269
2318
  const [editorMode, setEditorMode] = (0, import_react11.useState)("editor");
2270
2319
  const debouncedContent = useDebounce(value, debounceMs);
2320
+ useLazyTailwindCDN(tailwindCDN);
2271
2321
  const handleToggleFold = () => {
2272
2322
  if (!editorRef.current) return;
2273
2323
  if (isAllFolded) {
@@ -2315,36 +2365,67 @@ var NReditor = ({
2315
2365
  { key: "split", icon: "vertical_split", label: "Split" },
2316
2366
  { key: "preview", icon: "visibility", label: "Preview" }
2317
2367
  ];
2318
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `relative flex-1 flex flex-col min-h-0 ${className || ""}`, children: [
2319
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between px-2 py-1.5 border-b border-border/30 bg-background-secondary-solid/50 rounded-t-2xl shrink-0", children: [
2320
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2321
- "button",
2322
- {
2323
- onClick: handleToggleFold,
2324
- className: "p-1 px-2 text-xs font-bold bg-background-secondary-solid border border-border rounded hover:bg-border text-accent-primary transition-colors",
2325
- title: isAllFolded ? "Expand all" : "Collapse all",
2326
- children: isAllFolded ? "\u2569" : "\u2566"
2327
- }
2328
- ),
2329
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex items-center gap-0.5 bg-black/10 dark:bg-white/5 rounded-lg p-0.5", children: modeButtons.map((btn) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2330
- "button",
2331
- {
2332
- onClick: () => setEditorMode(btn.key),
2333
- className: `flex items-center gap-1 px-2.5 py-1 text-xs rounded-md transition-colors ${editorMode === btn.key ? "bg-accent-primary/20 text-accent-primary font-semibold" : "text-text-secondary hover:text-text-primary hover:bg-white/5"}`,
2334
- title: btn.label,
2335
- children: [
2336
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-rounded text-sm", children: btn.icon }),
2337
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "hidden sm:inline", children: btn.label })
2338
- ]
2339
- },
2340
- btn.key
2341
- )) })
2368
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `nr-editor ${className || ""}`, children: [
2369
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "nr-editor-toolbar", children: [
2370
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "nr-editor-toolbar-left", children: [
2371
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2372
+ "button",
2373
+ {
2374
+ onClick: handleToggleFold,
2375
+ className: "nr-toolbar-btn nr-toolbar-btn-fold",
2376
+ title: isAllFolded ? "Expandir todo" : "Colapsar todo",
2377
+ "aria-label": isAllFolded ? "Expandir todo" : "Colapsar todo",
2378
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-rounded", children: isAllFolded ? "unfold_more" : "unfold_less" })
2379
+ }
2380
+ ),
2381
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "nr-toolbar-divider" }),
2382
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "nr-toolbar-mode-group", children: modeButtons.map((btn) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2383
+ "button",
2384
+ {
2385
+ onClick: () => setEditorMode(btn.key),
2386
+ className: `nr-toolbar-btn nr-toolbar-btn-mode ${editorMode === btn.key ? "nr-toolbar-btn-mode--active" : ""} ${btn.key === "split" ? "nr-toolbar-btn-split" : ""}`,
2387
+ title: btn.label,
2388
+ children: [
2389
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-rounded", children: btn.icon }),
2390
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "nr-toolbar-label", children: btn.label })
2391
+ ]
2392
+ },
2393
+ btn.key
2394
+ )) })
2395
+ ] }),
2396
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "nr-editor-toolbar-right", children: [
2397
+ onGuide && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2398
+ "button",
2399
+ {
2400
+ onClick: onGuide,
2401
+ className: "nr-toolbar-btn nr-toolbar-btn-sm nr-toolbar-btn-guide",
2402
+ title: "Gu\xEDa de sintaxis",
2403
+ children: [
2404
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-rounded", children: "menu_book" }),
2405
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "nr-toolbar-label", children: "Gu\xEDa" })
2406
+ ]
2407
+ }
2408
+ ),
2409
+ onGuide && onConfig && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "nr-toolbar-divider" }),
2410
+ onConfig && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2411
+ "button",
2412
+ {
2413
+ onClick: onConfig,
2414
+ className: "nr-toolbar-btn nr-toolbar-btn-sm nr-toolbar-btn-config",
2415
+ title: "Configurar tema y metadata",
2416
+ children: [
2417
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-rounded", children: "tune" }),
2418
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "nr-toolbar-label", children: "Configurar" })
2419
+ ]
2420
+ }
2421
+ )
2422
+ ] })
2342
2423
  ] }),
2343
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 min-h-0 flex", children: [
2424
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "nr-editor-body", children: [
2344
2425
  (editorMode === "editor" || editorMode === "split") && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2345
2426
  "div",
2346
2427
  {
2347
- className: `${editorMode === "split" ? "w-1/2 border-r border-border/30" : "w-full"} flex-1 flex flex-col min-h-0 min-w-0`,
2428
+ className: `nr-editor-pane ${editorMode === "split" ? "nr-editor-pane--half" : ""}`,
2348
2429
  children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2349
2430
  import_react_codemirror.default,
2350
2431
  {
@@ -2367,8 +2448,8 @@ var NReditor = ({
2367
2448
  (editorMode === "preview" || editorMode === "split") && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2368
2449
  "div",
2369
2450
  {
2370
- className: `${editorMode === "split" ? "w-1/2" : "w-full"} flex-1 min-h-0 overflow-auto min-w-0`,
2371
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "nr-prose h-full shadow-xs overflow-auto p-4", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CustomMarkdownRenderer_default, { content: debouncedContent }) })
2451
+ className: `nr-editor-preview ${editorMode === "split" ? "nr-editor-preview--half" : ""}`,
2452
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "nr-editor-prose", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "nr-prose", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CustomMarkdownRenderer_default, { content: debouncedContent }) }) })
2372
2453
  }
2373
2454
  )
2374
2455
  ] })