@miliastry/quasar 1.0.4 → 1.0.6
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/Visuals/lyne.css +83 -17
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +111 -9
- package/dist/index.mjs +111 -9
- package/package.json +1 -1
- package/src/BBCode/BBCodeToGreenNode.ts +17 -0
- package/src/Model/TagRegistry.ts +3 -0
- package/src/Tests/LyneMode.test.ts +1 -1
- package/src/Visitors/HTMLRenderer.ts +97 -10
- package/src/Visuals/lyne.css +83 -17
package/dist/Visuals/lyne.css
CHANGED
|
@@ -249,8 +249,8 @@
|
|
|
249
249
|
* del autor ([boxw=Title:#hex]) si lo hay; si no, el hairline del tema. */
|
|
250
250
|
.bbcode-preview-lyne details.boxw,
|
|
251
251
|
.bbcode-preview.theme-lyne details.boxw {
|
|
252
|
-
background: rgba(11, 16, 36, 0.5) !important;
|
|
253
|
-
border: 1px solid var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12))))) !important;
|
|
252
|
+
background: var(--box-bg, rgba(11, 16, 36, 0.5)) !important;
|
|
253
|
+
border: 1px solid var(--box-border, var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12)))))) !important;
|
|
254
254
|
border-radius: 0 !important;
|
|
255
255
|
clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px) !important;
|
|
256
256
|
padding: 0 !important;
|
|
@@ -331,8 +331,8 @@
|
|
|
331
331
|
width: 7px !important;
|
|
332
332
|
height: 7px !important;
|
|
333
333
|
flex: 0 0 7px !important;
|
|
334
|
-
border-right:
|
|
335
|
-
border-bottom:
|
|
334
|
+
border-right: 1px solid currentColor !important;
|
|
335
|
+
border-bottom: 1px solid currentColor !important;
|
|
336
336
|
transform: rotate(-45deg) !important;
|
|
337
337
|
transition: transform 0.15s ease !important;
|
|
338
338
|
}
|
|
@@ -360,7 +360,7 @@
|
|
|
360
360
|
|
|
361
361
|
.bbcode-preview-lyne details.boxw[open] > summary,
|
|
362
362
|
.bbcode-preview.theme-lyne details.boxw[open] > summary {
|
|
363
|
-
border-bottom: 1px solid var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12))))) !important;
|
|
363
|
+
border-bottom: 1px solid var(--box-border, var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12)))))) !important;
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
.bbcode-preview-lyne details[open] > summary::before,
|
|
@@ -370,7 +370,8 @@
|
|
|
370
370
|
|
|
371
371
|
.bbcode-preview-lyne details > summary:hover,
|
|
372
372
|
.bbcode-preview.theme-lyne details > summary:hover {
|
|
373
|
-
color: var(--color-accent-bright, #68FFF8) !important;
|
|
373
|
+
color: var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-accent-bright, #68FFF8)))) !important;
|
|
374
|
+
filter: brightness(1.25) !important;
|
|
374
375
|
}
|
|
375
376
|
|
|
376
377
|
/* ─── Lists & Markers ───────────────────────────────────────────────────── */
|
|
@@ -469,6 +470,7 @@
|
|
|
469
470
|
.bb-table {
|
|
470
471
|
width: 100% !important;
|
|
471
472
|
min-width: 380px !important;
|
|
473
|
+
table-layout: fixed !important;
|
|
472
474
|
border-collapse: separate !important;
|
|
473
475
|
border-spacing: 0 !important;
|
|
474
476
|
margin: 0 !important;
|
|
@@ -518,6 +520,64 @@
|
|
|
518
520
|
line-height: 1.3 !important;
|
|
519
521
|
text-transform: uppercase !important;
|
|
520
522
|
padding: 8px 10px !important;
|
|
523
|
+
text-align: left !important;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.bb-table .bb-th.bb-th-full {
|
|
527
|
+
width: 100% !important;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* Table header alignments */
|
|
531
|
+
.bb-table .bb-th.bb-th-left,
|
|
532
|
+
.bb-table .bb-th:has(.bb-left),
|
|
533
|
+
.bb-table .bb-th:has([style*="text-align:left"]),
|
|
534
|
+
.bb-table .bb-th:has([style*="text-align: left"]),
|
|
535
|
+
.bb-left .bb-table .bb-th {
|
|
536
|
+
text-align: left !important;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.bb-table .bb-th.bb-th-left .bb-table-badge,
|
|
540
|
+
.bb-table .bb-th:has(.bb-left) .bb-table-badge,
|
|
541
|
+
.bb-table .bb-th:has([style*="text-align:left"]) .bb-table-badge,
|
|
542
|
+
.bb-table .bb-th:has([style*="text-align: left"]) .bb-table-badge,
|
|
543
|
+
.bb-left .bb-table .bb-th .bb-table-badge {
|
|
544
|
+
margin-right: auto !important;
|
|
545
|
+
margin-left: 0 !important;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.bb-table .bb-th.bb-th-centered,
|
|
549
|
+
.bb-table .bb-th.bb-th-center,
|
|
550
|
+
.bb-table .bb-th:has(.bb-centre),
|
|
551
|
+
.bb-table .bb-th:has([style*="text-align:center"]),
|
|
552
|
+
.bb-table .bb-th:has([style*="text-align: center"]),
|
|
553
|
+
.bb-centre .bb-table .bb-th {
|
|
554
|
+
text-align: center !important;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.bb-table .bb-th.bb-th-centered .bb-table-badge,
|
|
558
|
+
.bb-table .bb-th.bb-th-center .bb-table-badge,
|
|
559
|
+
.bb-table .bb-th:has(.bb-centre) .bb-table-badge,
|
|
560
|
+
.bb-table .bb-th:has([style*="text-align:center"]) .bb-table-badge,
|
|
561
|
+
.bb-table .bb-th:has([style*="text-align: center"]) .bb-table-badge,
|
|
562
|
+
.bb-centre .bb-table .bb-th .bb-table-badge {
|
|
563
|
+
margin: 0 auto !important;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.bb-table .bb-th.bb-th-right,
|
|
567
|
+
.bb-table .bb-th:has(.bb-right),
|
|
568
|
+
.bb-table .bb-th:has([style*="text-align:right"]),
|
|
569
|
+
.bb-table .bb-th:has([style*="text-align: right"]),
|
|
570
|
+
.bb-right .bb-table .bb-th {
|
|
571
|
+
text-align: right !important;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.bb-table .bb-th.bb-th-right .bb-table-badge,
|
|
575
|
+
.bb-table .bb-th:has(.bb-right) .bb-table-badge,
|
|
576
|
+
.bb-table .bb-th:has([style*="text-align:right"]) .bb-table-badge,
|
|
577
|
+
.bb-table .bb-th:has([style*="text-align: right"]) .bb-table-badge,
|
|
578
|
+
.bb-right .bb-table .bb-th .bb-table-badge {
|
|
579
|
+
margin-left: auto !important;
|
|
580
|
+
margin-right: 0 !important;
|
|
521
581
|
}
|
|
522
582
|
|
|
523
583
|
.bb-table-badge {
|
|
@@ -610,7 +670,7 @@
|
|
|
610
670
|
border: 0 !important;
|
|
611
671
|
height: 1px !important;
|
|
612
672
|
background: linear-gradient(90deg, transparent, var(--color-accent, #2EE6E2), transparent) !important;
|
|
613
|
-
margin: 0 !important;
|
|
673
|
+
margin: 12px 0 !important;
|
|
614
674
|
text-align: center !important;
|
|
615
675
|
color: var(--color-accent, #2EE6E2) !important;
|
|
616
676
|
font-family: var(--font-numeric, monospace) !important;
|
|
@@ -733,7 +793,7 @@
|
|
|
733
793
|
.bb-neon-box {
|
|
734
794
|
padding: 16px !important;
|
|
735
795
|
margin: 0 !important;
|
|
736
|
-
border:
|
|
796
|
+
border: 1px solid var(--neon-color, var(--color-accent, #2EE6E2)) !important;
|
|
737
797
|
background: color-mix(in srgb, var(--neon-color, var(--color-accent, #2EE6E2)) 4%, rgba(7, 10, 23, 0.55)) !important;
|
|
738
798
|
border-radius: 0 !important;
|
|
739
799
|
box-shadow: none !important;
|
|
@@ -758,20 +818,26 @@
|
|
|
758
818
|
align-items: center !important;
|
|
759
819
|
}
|
|
760
820
|
|
|
761
|
-
/*
|
|
762
|
-
*
|
|
763
|
-
|
|
764
|
-
* badge. clip-path:none evita que herede los cortes de esquina de los paneles. */
|
|
821
|
+
/* Square: cuadradito badge con las muescas angulares características de Lyne.
|
|
822
|
+
* aspect-ratio: 1/1, centrado, borde hairline y corte angular de esquina. */
|
|
823
|
+
.bb-square,
|
|
765
824
|
.bb-circle {
|
|
766
825
|
display: inline-flex !important;
|
|
767
826
|
align-items: center !important;
|
|
768
827
|
justify-content: center !important;
|
|
769
|
-
aspect-ratio: 1 !important;
|
|
770
|
-
|
|
828
|
+
aspect-ratio: 1 / 1 !important;
|
|
829
|
+
min-width: 1.85em !important;
|
|
830
|
+
min-height: 1.85em !important;
|
|
831
|
+
padding: 0.2em 0.35em !important;
|
|
832
|
+
box-sizing: border-box !important;
|
|
833
|
+
border-radius: 0 !important;
|
|
771
834
|
overflow: hidden !important;
|
|
772
|
-
background: rgb(11 16 36 / 0.5) !important;
|
|
773
|
-
border: 1px solid var(--color-hairline, rgba(255, 255, 255, 0.12)) !important;
|
|
774
|
-
clip-path:
|
|
835
|
+
background: var(--square-bg, rgb(11 16 36 / 0.5)) !important;
|
|
836
|
+
border: 1px solid var(--square-border, var(--square-accent, var(--color-hairline, rgba(255, 255, 255, 0.12)))) !important;
|
|
837
|
+
clip-path: polygon(4px 0, 100% 0, 100% calc(100% - 4px), calc(100% - 4px) 100%, 0 100%, 0 4px) !important;
|
|
838
|
+
text-align: center !important;
|
|
839
|
+
line-height: 1 !important;
|
|
840
|
+
vertical-align: middle !important;
|
|
775
841
|
}
|
|
776
842
|
|
|
777
843
|
/* Stack: replica el .bbStack original de Lyne (columna flex con gap).
|
package/dist/index.d.mts
CHANGED
|
@@ -2458,6 +2458,8 @@ declare class HTMLRenderer extends Visitor<string> {
|
|
|
2458
2458
|
private static readonly CSS_FONT_RE;
|
|
2459
2459
|
/** Characters `escapeHtml` has to rewrite. Non-global on purpose: `test` must not carry `lastIndex`. */
|
|
2460
2460
|
private static readonly HTML_ESCAPE_RE;
|
|
2461
|
+
private static readonly BARE_HEX_RE;
|
|
2462
|
+
private static readonly NAMED_COLORS;
|
|
2461
2463
|
private sanitizeColor;
|
|
2462
2464
|
private sanitizeFontSize;
|
|
2463
2465
|
private sanitizeFontFamily;
|
|
@@ -2487,6 +2489,7 @@ declare class HTMLRenderer extends Visitor<string> {
|
|
|
2487
2489
|
private hexToRgba;
|
|
2488
2490
|
private renderNotice;
|
|
2489
2491
|
private renderTables;
|
|
2492
|
+
private getTableContext;
|
|
2490
2493
|
private renderGallery;
|
|
2491
2494
|
private renderColumns;
|
|
2492
2495
|
private renderSeparator;
|
package/dist/index.d.ts
CHANGED
|
@@ -2458,6 +2458,8 @@ declare class HTMLRenderer extends Visitor<string> {
|
|
|
2458
2458
|
private static readonly CSS_FONT_RE;
|
|
2459
2459
|
/** Characters `escapeHtml` has to rewrite. Non-global on purpose: `test` must not carry `lastIndex`. */
|
|
2460
2460
|
private static readonly HTML_ESCAPE_RE;
|
|
2461
|
+
private static readonly BARE_HEX_RE;
|
|
2462
|
+
private static readonly NAMED_COLORS;
|
|
2461
2463
|
private sanitizeColor;
|
|
2462
2464
|
private sanitizeFontSize;
|
|
2463
2465
|
private sanitizeFontFamily;
|
|
@@ -2487,6 +2489,7 @@ declare class HTMLRenderer extends Visitor<string> {
|
|
|
2487
2489
|
private hexToRgba;
|
|
2488
2490
|
private renderNotice;
|
|
2489
2491
|
private renderTables;
|
|
2492
|
+
private getTableContext;
|
|
2490
2493
|
private renderGallery;
|
|
2491
2494
|
private renderColumns;
|
|
2492
2495
|
private renderSeparator;
|
package/dist/index.js
CHANGED
|
@@ -1548,6 +1548,7 @@ var LYNE_LEGACY_ALIASES = {
|
|
|
1548
1548
|
"flex": "container",
|
|
1549
1549
|
"grid": "container",
|
|
1550
1550
|
"middle": "container",
|
|
1551
|
+
"square": "container",
|
|
1551
1552
|
"circle": "container",
|
|
1552
1553
|
"card": "container",
|
|
1553
1554
|
"glass": "container",
|
|
@@ -1711,6 +1712,22 @@ function extractGreenNodeMetadata(green) {
|
|
|
1711
1712
|
case "gradient":
|
|
1712
1713
|
const gradientColors = value.split(",").map((c) => c.trim()).filter((c) => c.startsWith("#"));
|
|
1713
1714
|
return gradientColors.length > 0 ? { colors: gradientColors } : {};
|
|
1715
|
+
case "table_th":
|
|
1716
|
+
case "table_col": {
|
|
1717
|
+
if (!value) return {};
|
|
1718
|
+
const parts = value.split(/[:,\s]+/).filter(Boolean);
|
|
1719
|
+
const res = {};
|
|
1720
|
+
for (const p of parts) {
|
|
1721
|
+
if (/^\d+$/.test(p)) {
|
|
1722
|
+
res.colspan = parseInt(p, 10);
|
|
1723
|
+
} else if (/^(center|centre|left|right|justify)$/i.test(p)) {
|
|
1724
|
+
res.align = p.toLowerCase() === "centre" ? "center" : p.toLowerCase();
|
|
1725
|
+
} else if (/^#[0-9a-fA-F]{3,8}$/.test(p)) {
|
|
1726
|
+
res.color = p;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
return res;
|
|
1730
|
+
}
|
|
1714
1731
|
case "notice":
|
|
1715
1732
|
case "wnotice":
|
|
1716
1733
|
return value ? { color: value } : {};
|
|
@@ -3635,6 +3652,9 @@ var TagRegistry = class {
|
|
|
3635
3652
|
{ name: "left", kind: "left", label: "Left", category: "layout", isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
3636
3653
|
{ name: "heading", kind: "heading", label: "Heading", category: "layout", isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
3637
3654
|
{ name: "notice", kind: "notice", label: "Notice", category: "layout", isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
3655
|
+
{ name: "wnotice", kind: "wnotice", label: "Warning Notice", category: "layout", isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
3656
|
+
{ name: "square", kind: "container", label: "Square Badge", category: "layout", isInline: true, isSelfClosing: false, canHaveChildren: true },
|
|
3657
|
+
{ name: "circle", kind: "container", label: "Square Badge", category: "layout", isInline: true, isSelfClosing: false, canHaveChildren: true, isDeprecated: true, deprecatedReplacement: "square" },
|
|
3638
3658
|
{ name: "spacing", kind: "spacing", label: "Spacing", category: "layout", isInline: false, isSelfClosing: true, canHaveChildren: false },
|
|
3639
3659
|
{ name: "empty_line", kind: "empty_line", label: "Empty Line", category: "layout", isInline: false, isSelfClosing: true, canHaveChildren: false },
|
|
3640
3660
|
// ── Media ──
|
|
@@ -5133,11 +5153,39 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5133
5153
|
return this.renderTables(node);
|
|
5134
5154
|
case "table_row":
|
|
5135
5155
|
return this.tableDepth > 0 ? this.wrapBlock("tr", node) : this.wrapInline("span", node);
|
|
5136
|
-
case "table_col":
|
|
5137
|
-
|
|
5156
|
+
case "table_col": {
|
|
5157
|
+
if (this.tableDepth <= 0) return this.wrapInline("span", node);
|
|
5158
|
+
const { cellCount, maxCols, isDirect } = this.getTableContext(node);
|
|
5159
|
+
let colspanAttr = "";
|
|
5160
|
+
if (node.metadata?.colspan) {
|
|
5161
|
+
colspanAttr = ` colspan="${node.metadata.colspan}"`;
|
|
5162
|
+
} else if (cellCount === 1 && maxCols > 1) {
|
|
5163
|
+
colspanAttr = ` colspan="${maxCols}"`;
|
|
5164
|
+
} else if (cellCount < maxCols && maxCols % cellCount === 0) {
|
|
5165
|
+
colspanAttr = ` colspan="${maxCols / cellCount}"`;
|
|
5166
|
+
}
|
|
5167
|
+
const align = node.metadata?.align ? ` style="text-align:${node.metadata.align};"` : "";
|
|
5168
|
+
const td = `<td${this.idAttr(node)}${colspanAttr}${align}>${this.renderChildren(node)}</td>`;
|
|
5169
|
+
return isDirect ? `<tr>${td}</tr>` : td;
|
|
5170
|
+
}
|
|
5138
5171
|
case "table_th": {
|
|
5139
5172
|
const content = this.renderChildren(node);
|
|
5140
|
-
|
|
5173
|
+
if (this.tableDepth <= 0) return this.wrapInline("span", node);
|
|
5174
|
+
const { cellCount, maxCols, isDirect } = this.getTableContext(node);
|
|
5175
|
+
let colspanAttr = "";
|
|
5176
|
+
let fullCls = "";
|
|
5177
|
+
if (node.metadata?.colspan) {
|
|
5178
|
+
colspanAttr = ` colspan="${node.metadata.colspan}"`;
|
|
5179
|
+
} else if (cellCount === 1) {
|
|
5180
|
+
colspanAttr = maxCols > 1 ? ` colspan="${maxCols}"` : ' colspan="100"';
|
|
5181
|
+
fullCls = " bb-th-full";
|
|
5182
|
+
} else if (cellCount < maxCols && maxCols % cellCount === 0) {
|
|
5183
|
+
colspanAttr = ` colspan="${maxCols / cellCount}"`;
|
|
5184
|
+
}
|
|
5185
|
+
const alignCls = node.metadata?.align ? ` bb-th-${node.metadata.align}` : "";
|
|
5186
|
+
const alignStyle = node.metadata?.align ? ` style="text-align:${node.metadata.align};"` : "";
|
|
5187
|
+
const th = `<th${this.idAttr(node)}${colspanAttr} class="bb-th${fullCls}${alignCls}"${alignStyle}><span class="bb-table-badge">${content}</span></th>`;
|
|
5188
|
+
return isDirect ? `<tr>${th}</tr>` : th;
|
|
5141
5189
|
}
|
|
5142
5190
|
case "gallery":
|
|
5143
5191
|
return this.renderGallery(node);
|
|
@@ -5311,8 +5359,33 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5311
5359
|
static CSS_FONT_RE = /^[A-Za-z0-9 ,_-]{1,120}$/;
|
|
5312
5360
|
/** Characters `escapeHtml` has to rewrite. Non-global on purpose: `test` must not carry `lastIndex`. */
|
|
5313
5361
|
static HTML_ESCAPE_RE = /[&<>"']/;
|
|
5362
|
+
static BARE_HEX_RE = /^[0-9a-f]{3,8}$/i;
|
|
5363
|
+
static NAMED_COLORS = {
|
|
5364
|
+
black: "#000000",
|
|
5365
|
+
white: "#FFFFFF",
|
|
5366
|
+
red: "#FF4C4C",
|
|
5367
|
+
green: "#2ECC71",
|
|
5368
|
+
blue: "#3498DB",
|
|
5369
|
+
yellow: "#F1C40F",
|
|
5370
|
+
cyan: "#2EE6E2",
|
|
5371
|
+
magenta: "#E056FD",
|
|
5372
|
+
pink: "#FF94C4",
|
|
5373
|
+
purple: "#A972FF",
|
|
5374
|
+
orange: "#FF9F43",
|
|
5375
|
+
amber: "#FFC34D",
|
|
5376
|
+
gray: "#8395A7",
|
|
5377
|
+
grey: "#8395A7",
|
|
5378
|
+
lime: "#10AC84",
|
|
5379
|
+
teal: "#01CBC6",
|
|
5380
|
+
violet: "#5F27CD",
|
|
5381
|
+
gold: "#FFD700"
|
|
5382
|
+
};
|
|
5314
5383
|
sanitizeColor(raw) {
|
|
5315
|
-
|
|
5384
|
+
let v = raw.trim();
|
|
5385
|
+
if (!v) return null;
|
|
5386
|
+
if (_HTMLRenderer.BARE_HEX_RE.test(v) && (v.length === 3 || v.length === 4 || v.length === 6 || v.length === 8)) {
|
|
5387
|
+
v = "#" + v;
|
|
5388
|
+
}
|
|
5316
5389
|
return _HTMLRenderer.CSS_COLOR_RE.test(v) ? v : null;
|
|
5317
5390
|
}
|
|
5318
5391
|
sanitizeFontSize(raw) {
|
|
@@ -5448,8 +5521,12 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5448
5521
|
return `<audio${this.idAttr(node)} controls src="${this.escapeHtml(src)}" class="bb-audio"></audio>`;
|
|
5449
5522
|
}
|
|
5450
5523
|
hexToRgba(hex, alpha) {
|
|
5451
|
-
|
|
5452
|
-
|
|
5524
|
+
let clean = hex.trim().toLowerCase();
|
|
5525
|
+
if (_HTMLRenderer.NAMED_COLORS[clean]) {
|
|
5526
|
+
clean = _HTMLRenderer.NAMED_COLORS[clean];
|
|
5527
|
+
}
|
|
5528
|
+
if (!clean.startsWith("#")) return clean;
|
|
5529
|
+
const t = clean.replace("#", "");
|
|
5453
5530
|
const full = t.length <= 4 ? t.split("").map((c) => c + c).join("") : t;
|
|
5454
5531
|
const r = parseInt(full.slice(0, 2), 16) || 0;
|
|
5455
5532
|
const g = parseInt(full.slice(2, 4), 16) || 0;
|
|
@@ -5482,6 +5559,22 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5482
5559
|
const accent = this.boxAccentStyle(node, "table");
|
|
5483
5560
|
return `<div${this.idAttr(node)} class="bb-table-frame"${accent}><table class="${tableClasses}"><tbody>${content}</tbody></table></div>`;
|
|
5484
5561
|
}
|
|
5562
|
+
getTableContext(node) {
|
|
5563
|
+
const parent = node.parent;
|
|
5564
|
+
const isDirect = parent?.kind === "tables";
|
|
5565
|
+
const tableNode = isDirect ? parent : parent?.parent?.kind === "tables" ? parent.parent : void 0;
|
|
5566
|
+
const cellCount = parent?.children ? parent.children.filter((c) => c.kind === "table_th" || c.kind === "table_col").length : 1;
|
|
5567
|
+
let maxCols = cellCount;
|
|
5568
|
+
if (tableNode) {
|
|
5569
|
+
for (const row of tableNode.children) {
|
|
5570
|
+
if (row.kind === "table_row") {
|
|
5571
|
+
const count = row.children.filter((c) => c.kind === "table_th" || c.kind === "table_col").length;
|
|
5572
|
+
if (count > maxCols) maxCols = count;
|
|
5573
|
+
}
|
|
5574
|
+
}
|
|
5575
|
+
}
|
|
5576
|
+
return { cellCount, maxCols, isDirect };
|
|
5577
|
+
}
|
|
5485
5578
|
renderGallery(node) {
|
|
5486
5579
|
const content = this.renderChildren(node);
|
|
5487
5580
|
return `<div${this.idAttr(node)} class="bb-gallery">${content}</div>`;
|
|
@@ -5627,8 +5720,14 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5627
5720
|
}
|
|
5628
5721
|
case "middle":
|
|
5629
5722
|
return `<div${idAttr} class="bb-middle">${content}</div>`;
|
|
5630
|
-
case "
|
|
5631
|
-
|
|
5723
|
+
case "square":
|
|
5724
|
+
case "circle": {
|
|
5725
|
+
const accent = this.sanitizeColor(param);
|
|
5726
|
+
const border = accent ? this.hexToRgba(accent, 0.35) : "";
|
|
5727
|
+
const bg = accent ? this.hexToRgba(accent, 0.08) : "";
|
|
5728
|
+
const style = accent ? ` style="--square-accent:${accent};--square-border:${border};--square-bg:${bg};"` : "";
|
|
5729
|
+
return `<div${idAttr} class="bb-square bb-cut-panel"${style}>${content}</div>`;
|
|
5730
|
+
}
|
|
5632
5731
|
case "card":
|
|
5633
5732
|
case "glass": {
|
|
5634
5733
|
const accent = this.sanitizeColor(param);
|
|
@@ -5740,7 +5839,10 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5740
5839
|
*/
|
|
5741
5840
|
boxAccentStyle(node, suffix = "box") {
|
|
5742
5841
|
const color = this.sanitizeColor(String(node.metadata?.color ?? ""));
|
|
5743
|
-
|
|
5842
|
+
if (!color) return "";
|
|
5843
|
+
const border = this.hexToRgba(color, 0.35);
|
|
5844
|
+
const bg = this.hexToRgba(color, 0.08);
|
|
5845
|
+
return ` style="--${suffix}-accent:${color};--${suffix}-border:${border};--${suffix}-bg:${bg};"`;
|
|
5744
5846
|
}
|
|
5745
5847
|
renderList(node) {
|
|
5746
5848
|
const attrs = this.extractValue(node);
|
package/dist/index.mjs
CHANGED
|
@@ -1546,6 +1546,7 @@ var LYNE_LEGACY_ALIASES = {
|
|
|
1546
1546
|
"flex": "container",
|
|
1547
1547
|
"grid": "container",
|
|
1548
1548
|
"middle": "container",
|
|
1549
|
+
"square": "container",
|
|
1549
1550
|
"circle": "container",
|
|
1550
1551
|
"card": "container",
|
|
1551
1552
|
"glass": "container",
|
|
@@ -1709,6 +1710,22 @@ function extractGreenNodeMetadata(green) {
|
|
|
1709
1710
|
case "gradient":
|
|
1710
1711
|
const gradientColors = value.split(",").map((c) => c.trim()).filter((c) => c.startsWith("#"));
|
|
1711
1712
|
return gradientColors.length > 0 ? { colors: gradientColors } : {};
|
|
1713
|
+
case "table_th":
|
|
1714
|
+
case "table_col": {
|
|
1715
|
+
if (!value) return {};
|
|
1716
|
+
const parts = value.split(/[:,\s]+/).filter(Boolean);
|
|
1717
|
+
const res = {};
|
|
1718
|
+
for (const p of parts) {
|
|
1719
|
+
if (/^\d+$/.test(p)) {
|
|
1720
|
+
res.colspan = parseInt(p, 10);
|
|
1721
|
+
} else if (/^(center|centre|left|right|justify)$/i.test(p)) {
|
|
1722
|
+
res.align = p.toLowerCase() === "centre" ? "center" : p.toLowerCase();
|
|
1723
|
+
} else if (/^#[0-9a-fA-F]{3,8}$/.test(p)) {
|
|
1724
|
+
res.color = p;
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
return res;
|
|
1728
|
+
}
|
|
1712
1729
|
case "notice":
|
|
1713
1730
|
case "wnotice":
|
|
1714
1731
|
return value ? { color: value } : {};
|
|
@@ -3633,6 +3650,9 @@ var TagRegistry = class {
|
|
|
3633
3650
|
{ name: "left", kind: "left", label: "Left", category: "layout", isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
3634
3651
|
{ name: "heading", kind: "heading", label: "Heading", category: "layout", isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
3635
3652
|
{ name: "notice", kind: "notice", label: "Notice", category: "layout", isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
3653
|
+
{ name: "wnotice", kind: "wnotice", label: "Warning Notice", category: "layout", isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
3654
|
+
{ name: "square", kind: "container", label: "Square Badge", category: "layout", isInline: true, isSelfClosing: false, canHaveChildren: true },
|
|
3655
|
+
{ name: "circle", kind: "container", label: "Square Badge", category: "layout", isInline: true, isSelfClosing: false, canHaveChildren: true, isDeprecated: true, deprecatedReplacement: "square" },
|
|
3636
3656
|
{ name: "spacing", kind: "spacing", label: "Spacing", category: "layout", isInline: false, isSelfClosing: true, canHaveChildren: false },
|
|
3637
3657
|
{ name: "empty_line", kind: "empty_line", label: "Empty Line", category: "layout", isInline: false, isSelfClosing: true, canHaveChildren: false },
|
|
3638
3658
|
// ── Media ──
|
|
@@ -5131,11 +5151,39 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5131
5151
|
return this.renderTables(node);
|
|
5132
5152
|
case "table_row":
|
|
5133
5153
|
return this.tableDepth > 0 ? this.wrapBlock("tr", node) : this.wrapInline("span", node);
|
|
5134
|
-
case "table_col":
|
|
5135
|
-
|
|
5154
|
+
case "table_col": {
|
|
5155
|
+
if (this.tableDepth <= 0) return this.wrapInline("span", node);
|
|
5156
|
+
const { cellCount, maxCols, isDirect } = this.getTableContext(node);
|
|
5157
|
+
let colspanAttr = "";
|
|
5158
|
+
if (node.metadata?.colspan) {
|
|
5159
|
+
colspanAttr = ` colspan="${node.metadata.colspan}"`;
|
|
5160
|
+
} else if (cellCount === 1 && maxCols > 1) {
|
|
5161
|
+
colspanAttr = ` colspan="${maxCols}"`;
|
|
5162
|
+
} else if (cellCount < maxCols && maxCols % cellCount === 0) {
|
|
5163
|
+
colspanAttr = ` colspan="${maxCols / cellCount}"`;
|
|
5164
|
+
}
|
|
5165
|
+
const align = node.metadata?.align ? ` style="text-align:${node.metadata.align};"` : "";
|
|
5166
|
+
const td = `<td${this.idAttr(node)}${colspanAttr}${align}>${this.renderChildren(node)}</td>`;
|
|
5167
|
+
return isDirect ? `<tr>${td}</tr>` : td;
|
|
5168
|
+
}
|
|
5136
5169
|
case "table_th": {
|
|
5137
5170
|
const content = this.renderChildren(node);
|
|
5138
|
-
|
|
5171
|
+
if (this.tableDepth <= 0) return this.wrapInline("span", node);
|
|
5172
|
+
const { cellCount, maxCols, isDirect } = this.getTableContext(node);
|
|
5173
|
+
let colspanAttr = "";
|
|
5174
|
+
let fullCls = "";
|
|
5175
|
+
if (node.metadata?.colspan) {
|
|
5176
|
+
colspanAttr = ` colspan="${node.metadata.colspan}"`;
|
|
5177
|
+
} else if (cellCount === 1) {
|
|
5178
|
+
colspanAttr = maxCols > 1 ? ` colspan="${maxCols}"` : ' colspan="100"';
|
|
5179
|
+
fullCls = " bb-th-full";
|
|
5180
|
+
} else if (cellCount < maxCols && maxCols % cellCount === 0) {
|
|
5181
|
+
colspanAttr = ` colspan="${maxCols / cellCount}"`;
|
|
5182
|
+
}
|
|
5183
|
+
const alignCls = node.metadata?.align ? ` bb-th-${node.metadata.align}` : "";
|
|
5184
|
+
const alignStyle = node.metadata?.align ? ` style="text-align:${node.metadata.align};"` : "";
|
|
5185
|
+
const th = `<th${this.idAttr(node)}${colspanAttr} class="bb-th${fullCls}${alignCls}"${alignStyle}><span class="bb-table-badge">${content}</span></th>`;
|
|
5186
|
+
return isDirect ? `<tr>${th}</tr>` : th;
|
|
5139
5187
|
}
|
|
5140
5188
|
case "gallery":
|
|
5141
5189
|
return this.renderGallery(node);
|
|
@@ -5309,8 +5357,33 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5309
5357
|
static CSS_FONT_RE = /^[A-Za-z0-9 ,_-]{1,120}$/;
|
|
5310
5358
|
/** Characters `escapeHtml` has to rewrite. Non-global on purpose: `test` must not carry `lastIndex`. */
|
|
5311
5359
|
static HTML_ESCAPE_RE = /[&<>"']/;
|
|
5360
|
+
static BARE_HEX_RE = /^[0-9a-f]{3,8}$/i;
|
|
5361
|
+
static NAMED_COLORS = {
|
|
5362
|
+
black: "#000000",
|
|
5363
|
+
white: "#FFFFFF",
|
|
5364
|
+
red: "#FF4C4C",
|
|
5365
|
+
green: "#2ECC71",
|
|
5366
|
+
blue: "#3498DB",
|
|
5367
|
+
yellow: "#F1C40F",
|
|
5368
|
+
cyan: "#2EE6E2",
|
|
5369
|
+
magenta: "#E056FD",
|
|
5370
|
+
pink: "#FF94C4",
|
|
5371
|
+
purple: "#A972FF",
|
|
5372
|
+
orange: "#FF9F43",
|
|
5373
|
+
amber: "#FFC34D",
|
|
5374
|
+
gray: "#8395A7",
|
|
5375
|
+
grey: "#8395A7",
|
|
5376
|
+
lime: "#10AC84",
|
|
5377
|
+
teal: "#01CBC6",
|
|
5378
|
+
violet: "#5F27CD",
|
|
5379
|
+
gold: "#FFD700"
|
|
5380
|
+
};
|
|
5312
5381
|
sanitizeColor(raw) {
|
|
5313
|
-
|
|
5382
|
+
let v = raw.trim();
|
|
5383
|
+
if (!v) return null;
|
|
5384
|
+
if (_HTMLRenderer.BARE_HEX_RE.test(v) && (v.length === 3 || v.length === 4 || v.length === 6 || v.length === 8)) {
|
|
5385
|
+
v = "#" + v;
|
|
5386
|
+
}
|
|
5314
5387
|
return _HTMLRenderer.CSS_COLOR_RE.test(v) ? v : null;
|
|
5315
5388
|
}
|
|
5316
5389
|
sanitizeFontSize(raw) {
|
|
@@ -5446,8 +5519,12 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5446
5519
|
return `<audio${this.idAttr(node)} controls src="${this.escapeHtml(src)}" class="bb-audio"></audio>`;
|
|
5447
5520
|
}
|
|
5448
5521
|
hexToRgba(hex, alpha) {
|
|
5449
|
-
|
|
5450
|
-
|
|
5522
|
+
let clean = hex.trim().toLowerCase();
|
|
5523
|
+
if (_HTMLRenderer.NAMED_COLORS[clean]) {
|
|
5524
|
+
clean = _HTMLRenderer.NAMED_COLORS[clean];
|
|
5525
|
+
}
|
|
5526
|
+
if (!clean.startsWith("#")) return clean;
|
|
5527
|
+
const t = clean.replace("#", "");
|
|
5451
5528
|
const full = t.length <= 4 ? t.split("").map((c) => c + c).join("") : t;
|
|
5452
5529
|
const r = parseInt(full.slice(0, 2), 16) || 0;
|
|
5453
5530
|
const g = parseInt(full.slice(2, 4), 16) || 0;
|
|
@@ -5480,6 +5557,22 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5480
5557
|
const accent = this.boxAccentStyle(node, "table");
|
|
5481
5558
|
return `<div${this.idAttr(node)} class="bb-table-frame"${accent}><table class="${tableClasses}"><tbody>${content}</tbody></table></div>`;
|
|
5482
5559
|
}
|
|
5560
|
+
getTableContext(node) {
|
|
5561
|
+
const parent = node.parent;
|
|
5562
|
+
const isDirect = parent?.kind === "tables";
|
|
5563
|
+
const tableNode = isDirect ? parent : parent?.parent?.kind === "tables" ? parent.parent : void 0;
|
|
5564
|
+
const cellCount = parent?.children ? parent.children.filter((c) => c.kind === "table_th" || c.kind === "table_col").length : 1;
|
|
5565
|
+
let maxCols = cellCount;
|
|
5566
|
+
if (tableNode) {
|
|
5567
|
+
for (const row of tableNode.children) {
|
|
5568
|
+
if (row.kind === "table_row") {
|
|
5569
|
+
const count = row.children.filter((c) => c.kind === "table_th" || c.kind === "table_col").length;
|
|
5570
|
+
if (count > maxCols) maxCols = count;
|
|
5571
|
+
}
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
return { cellCount, maxCols, isDirect };
|
|
5575
|
+
}
|
|
5483
5576
|
renderGallery(node) {
|
|
5484
5577
|
const content = this.renderChildren(node);
|
|
5485
5578
|
return `<div${this.idAttr(node)} class="bb-gallery">${content}</div>`;
|
|
@@ -5625,8 +5718,14 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5625
5718
|
}
|
|
5626
5719
|
case "middle":
|
|
5627
5720
|
return `<div${idAttr} class="bb-middle">${content}</div>`;
|
|
5628
|
-
case "
|
|
5629
|
-
|
|
5721
|
+
case "square":
|
|
5722
|
+
case "circle": {
|
|
5723
|
+
const accent = this.sanitizeColor(param);
|
|
5724
|
+
const border = accent ? this.hexToRgba(accent, 0.35) : "";
|
|
5725
|
+
const bg = accent ? this.hexToRgba(accent, 0.08) : "";
|
|
5726
|
+
const style = accent ? ` style="--square-accent:${accent};--square-border:${border};--square-bg:${bg};"` : "";
|
|
5727
|
+
return `<div${idAttr} class="bb-square bb-cut-panel"${style}>${content}</div>`;
|
|
5728
|
+
}
|
|
5630
5729
|
case "card":
|
|
5631
5730
|
case "glass": {
|
|
5632
5731
|
const accent = this.sanitizeColor(param);
|
|
@@ -5738,7 +5837,10 @@ var HTMLRenderer = class _HTMLRenderer extends Visitor {
|
|
|
5738
5837
|
*/
|
|
5739
5838
|
boxAccentStyle(node, suffix = "box") {
|
|
5740
5839
|
const color = this.sanitizeColor(String(node.metadata?.color ?? ""));
|
|
5741
|
-
|
|
5840
|
+
if (!color) return "";
|
|
5841
|
+
const border = this.hexToRgba(color, 0.35);
|
|
5842
|
+
const bg = this.hexToRgba(color, 0.08);
|
|
5843
|
+
return ` style="--${suffix}-accent:${color};--${suffix}-border:${border};--${suffix}-bg:${bg};"`;
|
|
5742
5844
|
}
|
|
5743
5845
|
renderList(node) {
|
|
5744
5846
|
const attrs = this.extractValue(node);
|
package/package.json
CHANGED
|
@@ -188,6 +188,7 @@ const LYNE_LEGACY_ALIASES: Record<string, NodeKind> = {
|
|
|
188
188
|
'flex': 'container',
|
|
189
189
|
'grid': 'container',
|
|
190
190
|
'middle': 'container',
|
|
191
|
+
'square': 'container',
|
|
191
192
|
'circle': 'container',
|
|
192
193
|
'card': 'container',
|
|
193
194
|
'glass': 'container',
|
|
@@ -441,6 +442,22 @@ export function extractGreenNodeMetadata(green: GreenNode): Record<string, unkno
|
|
|
441
442
|
// Parse "=#ff0000,#00ff00" → { colors: ['#FF0000', '#00FF00'] }
|
|
442
443
|
const gradientColors = value.split(',').map(c => c.trim()).filter(c => c.startsWith('#'))
|
|
443
444
|
return gradientColors.length > 0 ? { colors: gradientColors } : {}
|
|
445
|
+
case 'table_th':
|
|
446
|
+
case 'table_col': {
|
|
447
|
+
if (!value) return {}
|
|
448
|
+
const parts = value.split(/[:,\s]+/).filter(Boolean)
|
|
449
|
+
const res: Record<string, any> = {}
|
|
450
|
+
for (const p of parts) {
|
|
451
|
+
if (/^\d+$/.test(p)) {
|
|
452
|
+
res.colspan = parseInt(p, 10)
|
|
453
|
+
} else if (/^(center|centre|left|right|justify)$/i.test(p)) {
|
|
454
|
+
res.align = p.toLowerCase() === 'centre' ? 'center' : p.toLowerCase()
|
|
455
|
+
} else if (/^#[0-9a-fA-F]{3,8}$/.test(p)) {
|
|
456
|
+
res.color = p
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return res
|
|
460
|
+
}
|
|
444
461
|
case 'notice':
|
|
445
462
|
case 'wnotice': return value ? { color: value } : {}
|
|
446
463
|
case 'tables':
|
package/src/Model/TagRegistry.ts
CHANGED
|
@@ -464,6 +464,9 @@ export class TagRegistry {
|
|
|
464
464
|
{ name: 'left', kind: 'left', label: 'Left', category: 'layout', isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
465
465
|
{ name: 'heading', kind: 'heading', label: 'Heading', category: 'layout', isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
466
466
|
{ name: 'notice', kind: 'notice', label: 'Notice', category: 'layout', isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
467
|
+
{ name: 'wnotice', kind: 'wnotice', label: 'Warning Notice', category: 'layout', isInline: false, isSelfClosing: false, canHaveChildren: true },
|
|
468
|
+
{ name: 'square', kind: 'container', label: 'Square Badge', category: 'layout', isInline: true, isSelfClosing: false, canHaveChildren: true },
|
|
469
|
+
{ name: 'circle', kind: 'container', label: 'Square Badge', category: 'layout', isInline: true, isSelfClosing: false, canHaveChildren: true, isDeprecated: true, deprecatedReplacement: 'square' },
|
|
467
470
|
{ name: 'spacing', kind: 'spacing', label: 'Spacing', category: 'layout', isInline: false, isSelfClosing: true, canHaveChildren: false },
|
|
468
471
|
{ name: 'empty_line', kind: 'empty_line', label: 'Empty Line', category: 'layout', isInline: false, isSelfClosing: true, canHaveChildren: false },
|
|
469
472
|
|
|
@@ -110,7 +110,7 @@ describe('Lyne Mode & Dialect Isolation', () => {
|
|
|
110
110
|
|
|
111
111
|
const html = doc.toHTML()
|
|
112
112
|
expect(html).toContain('class="bb-table bb-table-striped bb-table-borders"')
|
|
113
|
-
expect(html).toContain('
|
|
113
|
+
expect(html).toContain('bb-th')
|
|
114
114
|
expect(html).toContain('class="bb-columns" style="column-count:3;"')
|
|
115
115
|
expect(html).toContain('class="bb-gallery"')
|
|
116
116
|
expect(html).toContain('✦ ✦ ✦')
|
|
@@ -304,12 +304,39 @@ export class HTMLRenderer extends Visitor<string> {
|
|
|
304
304
|
case 'align': return this.renderAlign(node)
|
|
305
305
|
case 'tables': return this.renderTables(node)
|
|
306
306
|
case 'table_row': return this.tableDepth > 0 ? this.wrapBlock('tr', node) : this.wrapInline('span', node)
|
|
307
|
-
case 'table_col':
|
|
307
|
+
case 'table_col': {
|
|
308
|
+
if (this.tableDepth <= 0) return this.wrapInline('span', node)
|
|
309
|
+
const { cellCount, maxCols, isDirect } = this.getTableContext(node)
|
|
310
|
+
let colspanAttr = ''
|
|
311
|
+
if (node.metadata?.colspan) {
|
|
312
|
+
colspanAttr = ` colspan="${node.metadata.colspan}"`
|
|
313
|
+
} else if (cellCount === 1 && maxCols > 1) {
|
|
314
|
+
colspanAttr = ` colspan="${maxCols}"`
|
|
315
|
+
} else if (cellCount < maxCols && maxCols % cellCount === 0) {
|
|
316
|
+
colspanAttr = ` colspan="${maxCols / cellCount}"`
|
|
317
|
+
}
|
|
318
|
+
const align = node.metadata?.align ? ` style="text-align:${node.metadata.align};"` : ''
|
|
319
|
+
const td = `<td${this.idAttr(node)}${colspanAttr}${align}>${this.renderChildren(node)}</td>`
|
|
320
|
+
return isDirect ? `<tr>${td}</tr>` : td
|
|
321
|
+
}
|
|
308
322
|
case 'table_th': {
|
|
309
323
|
const content = this.renderChildren(node)
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
324
|
+
if (this.tableDepth <= 0) return this.wrapInline('span', node)
|
|
325
|
+
const { cellCount, maxCols, isDirect } = this.getTableContext(node)
|
|
326
|
+
let colspanAttr = ''
|
|
327
|
+
let fullCls = ''
|
|
328
|
+
if (node.metadata?.colspan) {
|
|
329
|
+
colspanAttr = ` colspan="${node.metadata.colspan}"`
|
|
330
|
+
} else if (cellCount === 1) {
|
|
331
|
+
colspanAttr = maxCols > 1 ? ` colspan="${maxCols}"` : ' colspan="100"'
|
|
332
|
+
fullCls = ' bb-th-full'
|
|
333
|
+
} else if (cellCount < maxCols && maxCols % cellCount === 0) {
|
|
334
|
+
colspanAttr = ` colspan="${maxCols / cellCount}"`
|
|
335
|
+
}
|
|
336
|
+
const alignCls = node.metadata?.align ? ` bb-th-${node.metadata.align}` : ''
|
|
337
|
+
const alignStyle = node.metadata?.align ? ` style="text-align:${node.metadata.align};"` : ''
|
|
338
|
+
const th = `<th${this.idAttr(node)}${colspanAttr} class="bb-th${fullCls}${alignCls}"${alignStyle}><span class="bb-table-badge">${content}</span></th>`
|
|
339
|
+
return isDirect ? `<tr>${th}</tr>` : th
|
|
313
340
|
}
|
|
314
341
|
case 'gallery': return this.renderGallery(node)
|
|
315
342
|
case 'columns': return this.renderColumns(node)
|
|
@@ -475,8 +502,35 @@ export class HTMLRenderer extends Visitor<string> {
|
|
|
475
502
|
/** Characters `escapeHtml` has to rewrite. Non-global on purpose: `test` must not carry `lastIndex`. */
|
|
476
503
|
private static readonly HTML_ESCAPE_RE = /[&<>"']/
|
|
477
504
|
|
|
505
|
+
private static readonly BARE_HEX_RE = /^[0-9a-f]{3,8}$/i
|
|
506
|
+
|
|
507
|
+
private static readonly NAMED_COLORS: Record<string, string> = {
|
|
508
|
+
black: '#000000',
|
|
509
|
+
white: '#FFFFFF',
|
|
510
|
+
red: '#FF4C4C',
|
|
511
|
+
green: '#2ECC71',
|
|
512
|
+
blue: '#3498DB',
|
|
513
|
+
yellow: '#F1C40F',
|
|
514
|
+
cyan: '#2EE6E2',
|
|
515
|
+
magenta: '#E056FD',
|
|
516
|
+
pink: '#FF94C4',
|
|
517
|
+
purple: '#A972FF',
|
|
518
|
+
orange: '#FF9F43',
|
|
519
|
+
amber: '#FFC34D',
|
|
520
|
+
gray: '#8395A7',
|
|
521
|
+
grey: '#8395A7',
|
|
522
|
+
lime: '#10AC84',
|
|
523
|
+
teal: '#01CBC6',
|
|
524
|
+
violet: '#5F27CD',
|
|
525
|
+
gold: '#FFD700',
|
|
526
|
+
}
|
|
527
|
+
|
|
478
528
|
private sanitizeColor(raw: string): string | null {
|
|
479
|
-
|
|
529
|
+
let v = raw.trim()
|
|
530
|
+
if (!v) return null
|
|
531
|
+
if (HTMLRenderer.BARE_HEX_RE.test(v) && (v.length === 3 || v.length === 4 || v.length === 6 || v.length === 8)) {
|
|
532
|
+
v = '#' + v
|
|
533
|
+
}
|
|
480
534
|
return HTMLRenderer.CSS_COLOR_RE.test(v) ? v : null
|
|
481
535
|
}
|
|
482
536
|
|
|
@@ -637,8 +691,12 @@ export class HTMLRenderer extends Visitor<string> {
|
|
|
637
691
|
}
|
|
638
692
|
|
|
639
693
|
private hexToRgba(hex: string, alpha: number): string {
|
|
640
|
-
|
|
641
|
-
|
|
694
|
+
let clean = hex.trim().toLowerCase()
|
|
695
|
+
if (HTMLRenderer.NAMED_COLORS[clean]) {
|
|
696
|
+
clean = HTMLRenderer.NAMED_COLORS[clean]
|
|
697
|
+
}
|
|
698
|
+
if (!clean.startsWith('#')) return clean
|
|
699
|
+
const t = clean.replace('#', '')
|
|
642
700
|
const full = t.length <= 4 ? t.split('').map(c => c + c).join('') : t
|
|
643
701
|
const r = parseInt(full.slice(0, 2), 16) || 0
|
|
644
702
|
const g = parseInt(full.slice(2, 4), 16) || 0
|
|
@@ -679,6 +737,26 @@ export class HTMLRenderer extends Visitor<string> {
|
|
|
679
737
|
return `<div${this.idAttr(node)} class="bb-table-frame"${accent}><table class="${tableClasses}"><tbody>${content}</tbody></table></div>`
|
|
680
738
|
}
|
|
681
739
|
|
|
740
|
+
private getTableContext(node: RedNode): { cellCount: number; maxCols: number; isDirect: boolean } {
|
|
741
|
+
const parent = node.parent
|
|
742
|
+
const isDirect = parent?.kind === 'tables'
|
|
743
|
+
const tableNode = isDirect ? parent : (parent?.parent?.kind === 'tables' ? parent.parent : undefined)
|
|
744
|
+
const cellCount = parent?.children
|
|
745
|
+
? parent.children.filter(c => c.kind === 'table_th' || c.kind === 'table_col').length
|
|
746
|
+
: 1
|
|
747
|
+
|
|
748
|
+
let maxCols = cellCount
|
|
749
|
+
if (tableNode) {
|
|
750
|
+
for (const row of tableNode.children) {
|
|
751
|
+
if (row.kind === 'table_row') {
|
|
752
|
+
const count = row.children.filter(c => c.kind === 'table_th' || c.kind === 'table_col').length
|
|
753
|
+
if (count > maxCols) maxCols = count
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return { cellCount, maxCols, isDirect }
|
|
758
|
+
}
|
|
759
|
+
|
|
682
760
|
private renderGallery(node: RedNode): string {
|
|
683
761
|
const content = this.renderChildren(node)
|
|
684
762
|
return `<div${this.idAttr(node)} class="bb-gallery">${content}</div>`
|
|
@@ -824,8 +902,14 @@ export class HTMLRenderer extends Visitor<string> {
|
|
|
824
902
|
}
|
|
825
903
|
case 'middle':
|
|
826
904
|
return `<div${idAttr} class="bb-middle">${content}</div>`
|
|
827
|
-
case '
|
|
828
|
-
|
|
905
|
+
case 'square':
|
|
906
|
+
case 'circle': {
|
|
907
|
+
const accent = this.sanitizeColor(param)
|
|
908
|
+
const border = accent ? this.hexToRgba(accent, 0.35) : ''
|
|
909
|
+
const bg = accent ? this.hexToRgba(accent, 0.08) : ''
|
|
910
|
+
const style = accent ? ` style="--square-accent:${accent};--square-border:${border};--square-bg:${bg};"` : ''
|
|
911
|
+
return `<div${idAttr} class="bb-square bb-cut-panel"${style}>${content}</div>`
|
|
912
|
+
}
|
|
829
913
|
case 'card':
|
|
830
914
|
case 'glass': {
|
|
831
915
|
// `[card=#hex]` / `[container=glass:#hex]`: el color se pasa como
|
|
@@ -942,7 +1026,10 @@ export class HTMLRenderer extends Visitor<string> {
|
|
|
942
1026
|
*/
|
|
943
1027
|
private boxAccentStyle(node: RedNode, suffix: 'box' | 'table' | 'columns' = 'box'): string {
|
|
944
1028
|
const color = this.sanitizeColor(String(node.metadata?.color ?? ''))
|
|
945
|
-
|
|
1029
|
+
if (!color) return ''
|
|
1030
|
+
const border = this.hexToRgba(color, 0.35)
|
|
1031
|
+
const bg = this.hexToRgba(color, 0.08)
|
|
1032
|
+
return ` style="--${suffix}-accent:${color};--${suffix}-border:${border};--${suffix}-bg:${bg};"`
|
|
946
1033
|
}
|
|
947
1034
|
|
|
948
1035
|
private renderList(node: RedNode): string {
|
package/src/Visuals/lyne.css
CHANGED
|
@@ -249,8 +249,8 @@
|
|
|
249
249
|
* del autor ([boxw=Title:#hex]) si lo hay; si no, el hairline del tema. */
|
|
250
250
|
.bbcode-preview-lyne details.boxw,
|
|
251
251
|
.bbcode-preview.theme-lyne details.boxw {
|
|
252
|
-
background: rgba(11, 16, 36, 0.5) !important;
|
|
253
|
-
border: 1px solid var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12))))) !important;
|
|
252
|
+
background: var(--box-bg, rgba(11, 16, 36, 0.5)) !important;
|
|
253
|
+
border: 1px solid var(--box-border, var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12)))))) !important;
|
|
254
254
|
border-radius: 0 !important;
|
|
255
255
|
clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px) !important;
|
|
256
256
|
padding: 0 !important;
|
|
@@ -331,8 +331,8 @@
|
|
|
331
331
|
width: 7px !important;
|
|
332
332
|
height: 7px !important;
|
|
333
333
|
flex: 0 0 7px !important;
|
|
334
|
-
border-right:
|
|
335
|
-
border-bottom:
|
|
334
|
+
border-right: 1px solid currentColor !important;
|
|
335
|
+
border-bottom: 1px solid currentColor !important;
|
|
336
336
|
transform: rotate(-45deg) !important;
|
|
337
337
|
transition: transform 0.15s ease !important;
|
|
338
338
|
}
|
|
@@ -360,7 +360,7 @@
|
|
|
360
360
|
|
|
361
361
|
.bbcode-preview-lyne details.boxw[open] > summary,
|
|
362
362
|
.bbcode-preview.theme-lyne details.boxw[open] > summary {
|
|
363
|
-
border-bottom: 1px solid var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12))))) !important;
|
|
363
|
+
border-bottom: 1px solid var(--box-border, var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-hairline, rgba(255, 255, 255, 0.12)))))) !important;
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
.bbcode-preview-lyne details[open] > summary::before,
|
|
@@ -370,7 +370,8 @@
|
|
|
370
370
|
|
|
371
371
|
.bbcode-preview-lyne details > summary:hover,
|
|
372
372
|
.bbcode-preview.theme-lyne details > summary:hover {
|
|
373
|
-
color: var(--color-accent-bright, #68FFF8) !important;
|
|
373
|
+
color: var(--box-accent, var(--card-accent, var(--glass-accent, var(--color-accent-bright, #68FFF8)))) !important;
|
|
374
|
+
filter: brightness(1.25) !important;
|
|
374
375
|
}
|
|
375
376
|
|
|
376
377
|
/* ─── Lists & Markers ───────────────────────────────────────────────────── */
|
|
@@ -469,6 +470,7 @@
|
|
|
469
470
|
.bb-table {
|
|
470
471
|
width: 100% !important;
|
|
471
472
|
min-width: 380px !important;
|
|
473
|
+
table-layout: fixed !important;
|
|
472
474
|
border-collapse: separate !important;
|
|
473
475
|
border-spacing: 0 !important;
|
|
474
476
|
margin: 0 !important;
|
|
@@ -518,6 +520,64 @@
|
|
|
518
520
|
line-height: 1.3 !important;
|
|
519
521
|
text-transform: uppercase !important;
|
|
520
522
|
padding: 8px 10px !important;
|
|
523
|
+
text-align: left !important;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.bb-table .bb-th.bb-th-full {
|
|
527
|
+
width: 100% !important;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* Table header alignments */
|
|
531
|
+
.bb-table .bb-th.bb-th-left,
|
|
532
|
+
.bb-table .bb-th:has(.bb-left),
|
|
533
|
+
.bb-table .bb-th:has([style*="text-align:left"]),
|
|
534
|
+
.bb-table .bb-th:has([style*="text-align: left"]),
|
|
535
|
+
.bb-left .bb-table .bb-th {
|
|
536
|
+
text-align: left !important;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.bb-table .bb-th.bb-th-left .bb-table-badge,
|
|
540
|
+
.bb-table .bb-th:has(.bb-left) .bb-table-badge,
|
|
541
|
+
.bb-table .bb-th:has([style*="text-align:left"]) .bb-table-badge,
|
|
542
|
+
.bb-table .bb-th:has([style*="text-align: left"]) .bb-table-badge,
|
|
543
|
+
.bb-left .bb-table .bb-th .bb-table-badge {
|
|
544
|
+
margin-right: auto !important;
|
|
545
|
+
margin-left: 0 !important;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.bb-table .bb-th.bb-th-centered,
|
|
549
|
+
.bb-table .bb-th.bb-th-center,
|
|
550
|
+
.bb-table .bb-th:has(.bb-centre),
|
|
551
|
+
.bb-table .bb-th:has([style*="text-align:center"]),
|
|
552
|
+
.bb-table .bb-th:has([style*="text-align: center"]),
|
|
553
|
+
.bb-centre .bb-table .bb-th {
|
|
554
|
+
text-align: center !important;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.bb-table .bb-th.bb-th-centered .bb-table-badge,
|
|
558
|
+
.bb-table .bb-th.bb-th-center .bb-table-badge,
|
|
559
|
+
.bb-table .bb-th:has(.bb-centre) .bb-table-badge,
|
|
560
|
+
.bb-table .bb-th:has([style*="text-align:center"]) .bb-table-badge,
|
|
561
|
+
.bb-table .bb-th:has([style*="text-align: center"]) .bb-table-badge,
|
|
562
|
+
.bb-centre .bb-table .bb-th .bb-table-badge {
|
|
563
|
+
margin: 0 auto !important;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.bb-table .bb-th.bb-th-right,
|
|
567
|
+
.bb-table .bb-th:has(.bb-right),
|
|
568
|
+
.bb-table .bb-th:has([style*="text-align:right"]),
|
|
569
|
+
.bb-table .bb-th:has([style*="text-align: right"]),
|
|
570
|
+
.bb-right .bb-table .bb-th {
|
|
571
|
+
text-align: right !important;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
.bb-table .bb-th.bb-th-right .bb-table-badge,
|
|
575
|
+
.bb-table .bb-th:has(.bb-right) .bb-table-badge,
|
|
576
|
+
.bb-table .bb-th:has([style*="text-align:right"]) .bb-table-badge,
|
|
577
|
+
.bb-table .bb-th:has([style*="text-align: right"]) .bb-table-badge,
|
|
578
|
+
.bb-right .bb-table .bb-th .bb-table-badge {
|
|
579
|
+
margin-left: auto !important;
|
|
580
|
+
margin-right: 0 !important;
|
|
521
581
|
}
|
|
522
582
|
|
|
523
583
|
.bb-table-badge {
|
|
@@ -610,7 +670,7 @@
|
|
|
610
670
|
border: 0 !important;
|
|
611
671
|
height: 1px !important;
|
|
612
672
|
background: linear-gradient(90deg, transparent, var(--color-accent, #2EE6E2), transparent) !important;
|
|
613
|
-
margin: 0 !important;
|
|
673
|
+
margin: 12px 0 !important;
|
|
614
674
|
text-align: center !important;
|
|
615
675
|
color: var(--color-accent, #2EE6E2) !important;
|
|
616
676
|
font-family: var(--font-numeric, monospace) !important;
|
|
@@ -733,7 +793,7 @@
|
|
|
733
793
|
.bb-neon-box {
|
|
734
794
|
padding: 16px !important;
|
|
735
795
|
margin: 0 !important;
|
|
736
|
-
border:
|
|
796
|
+
border: 1px solid var(--neon-color, var(--color-accent, #2EE6E2)) !important;
|
|
737
797
|
background: color-mix(in srgb, var(--neon-color, var(--color-accent, #2EE6E2)) 4%, rgba(7, 10, 23, 0.55)) !important;
|
|
738
798
|
border-radius: 0 !important;
|
|
739
799
|
box-shadow: none !important;
|
|
@@ -758,20 +818,26 @@
|
|
|
758
818
|
align-items: center !important;
|
|
759
819
|
}
|
|
760
820
|
|
|
761
|
-
/*
|
|
762
|
-
*
|
|
763
|
-
|
|
764
|
-
* badge. clip-path:none evita que herede los cortes de esquina de los paneles. */
|
|
821
|
+
/* Square: cuadradito badge con las muescas angulares características de Lyne.
|
|
822
|
+
* aspect-ratio: 1/1, centrado, borde hairline y corte angular de esquina. */
|
|
823
|
+
.bb-square,
|
|
765
824
|
.bb-circle {
|
|
766
825
|
display: inline-flex !important;
|
|
767
826
|
align-items: center !important;
|
|
768
827
|
justify-content: center !important;
|
|
769
|
-
aspect-ratio: 1 !important;
|
|
770
|
-
|
|
828
|
+
aspect-ratio: 1 / 1 !important;
|
|
829
|
+
min-width: 1.85em !important;
|
|
830
|
+
min-height: 1.85em !important;
|
|
831
|
+
padding: 0.2em 0.35em !important;
|
|
832
|
+
box-sizing: border-box !important;
|
|
833
|
+
border-radius: 0 !important;
|
|
771
834
|
overflow: hidden !important;
|
|
772
|
-
background: rgb(11 16 36 / 0.5) !important;
|
|
773
|
-
border: 1px solid var(--color-hairline, rgba(255, 255, 255, 0.12)) !important;
|
|
774
|
-
clip-path:
|
|
835
|
+
background: var(--square-bg, rgb(11 16 36 / 0.5)) !important;
|
|
836
|
+
border: 1px solid var(--square-border, var(--square-accent, var(--color-hairline, rgba(255, 255, 255, 0.12)))) !important;
|
|
837
|
+
clip-path: polygon(4px 0, 100% 0, 100% calc(100% - 4px), calc(100% - 4px) 100%, 0 100%, 0 4px) !important;
|
|
838
|
+
text-align: center !important;
|
|
839
|
+
line-height: 1 !important;
|
|
840
|
+
vertical-align: middle !important;
|
|
775
841
|
}
|
|
776
842
|
|
|
777
843
|
/* Stack: replica el .bbStack original de Lyne (columna flex con gap).
|