@ox-content/vite-plugin 2.25.0 → 2.27.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/index.cjs +88 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +7 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +88 -30
- package/dist/index.mjs.map +1 -1
- package/dist/tabs.cjs +14 -154
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs2.mjs +14 -152
- package/dist/tabs2.mjs.map +1 -1
- package/dist/youtube.cjs +10 -89
- package/dist/youtube.cjs.map +1 -1
- package/dist/youtube2.mjs +10 -87
- package/dist/youtube2.mjs.map +1 -1
- package/package.json +2 -2
package/dist/tabs.cjs
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
const require_chunk = require("./chunk.cjs");
|
|
2
|
-
|
|
3
|
-
let rehype_parse = require("rehype-parse");
|
|
4
|
-
rehype_parse = require_chunk.__toESM(rehype_parse, 1);
|
|
5
|
-
let rehype_stringify = require("rehype-stringify");
|
|
6
|
-
rehype_stringify = require_chunk.__toESM(rehype_stringify, 1);
|
|
2
|
+
const require_napi = require("./napi.cjs");
|
|
7
3
|
//#region src/plugins/tabs.ts
|
|
8
4
|
/**
|
|
9
5
|
* Tabs Plugin - Pure CSS implementation
|
|
10
6
|
*
|
|
11
|
-
* Transforms <Tabs>/<Tab> components into accessible HTML
|
|
12
|
-
*
|
|
7
|
+
* Transforms <Tabs>/<Tab> components into accessible HTML with CSS :has()
|
|
8
|
+
* based tab switching (no JavaScript required).
|
|
9
|
+
*
|
|
10
|
+
* The HTML rewrite is performed in Rust (`transformTabsEmbeds` in
|
|
11
|
+
* @ox-content/napi), replacing the previous rehype parse/stringify round-trip.
|
|
12
|
+
* The per-build group counter (consumed by `generateTabsCSS`) stays here and is
|
|
13
|
+
* advanced by the number of groups the Rust transform reports, so CSS
|
|
14
|
+
* generation still covers exactly the groups that were emitted.
|
|
13
15
|
*/
|
|
14
16
|
var tabs_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
15
17
|
generateTabsCSS: () => generateTabsCSS,
|
|
@@ -18,161 +20,19 @@ var tabs_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
|
18
20
|
});
|
|
19
21
|
let tabGroupCounter = 0;
|
|
20
22
|
/**
|
|
21
|
-
* Reset tab group counter (for testing).
|
|
23
|
+
* Reset tab group counter (for testing and per dev-server request).
|
|
22
24
|
*/
|
|
23
25
|
function resetTabGroupCounter() {
|
|
24
26
|
tabGroupCounter = 0;
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
|
-
* Get element attribute value.
|
|
28
|
-
*/
|
|
29
|
-
function getAttribute(el, name) {
|
|
30
|
-
const value = el.properties?.[name];
|
|
31
|
-
if (typeof value === "string") return value;
|
|
32
|
-
if (Array.isArray(value)) return value.join(" ");
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Parse Tab elements from Tabs children.
|
|
36
|
-
*/
|
|
37
|
-
function parseTabChildren(children) {
|
|
38
|
-
const tabs = [];
|
|
39
|
-
for (const child of children) {
|
|
40
|
-
if (child.type !== "element") continue;
|
|
41
|
-
if (child.tagName.toLowerCase() === "tab") {
|
|
42
|
-
const label = getAttribute(child, "label") || `Tab ${tabs.length + 1}`;
|
|
43
|
-
tabs.push({
|
|
44
|
-
label,
|
|
45
|
-
content: child.children.filter((c) => c.type === "element" || c.type === "text")
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return tabs;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Create the HTML structure for tabs.
|
|
53
|
-
*/
|
|
54
|
-
function createTabsElement(tabs, groupId) {
|
|
55
|
-
const children = [];
|
|
56
|
-
const headerChildren = [];
|
|
57
|
-
tabs.forEach((tab, index) => {
|
|
58
|
-
const inputId = `ox-tab-${groupId}-${index}`;
|
|
59
|
-
headerChildren.push({
|
|
60
|
-
type: "element",
|
|
61
|
-
tagName: "input",
|
|
62
|
-
properties: {
|
|
63
|
-
type: "radio",
|
|
64
|
-
name: `ox-tabs-${groupId}`,
|
|
65
|
-
id: inputId,
|
|
66
|
-
checked: index === 0 ? true : void 0
|
|
67
|
-
},
|
|
68
|
-
children: []
|
|
69
|
-
});
|
|
70
|
-
headerChildren.push({
|
|
71
|
-
type: "element",
|
|
72
|
-
tagName: "label",
|
|
73
|
-
properties: { htmlFor: inputId },
|
|
74
|
-
children: [{
|
|
75
|
-
type: "text",
|
|
76
|
-
value: tab.label
|
|
77
|
-
}]
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
children.push({
|
|
81
|
-
type: "element",
|
|
82
|
-
tagName: "div",
|
|
83
|
-
properties: { className: ["ox-tabs-header"] },
|
|
84
|
-
children: headerChildren
|
|
85
|
-
});
|
|
86
|
-
tabs.forEach((tab, index) => {
|
|
87
|
-
children.push({
|
|
88
|
-
type: "element",
|
|
89
|
-
tagName: "div",
|
|
90
|
-
properties: {
|
|
91
|
-
className: ["ox-tab-panel"],
|
|
92
|
-
"data-tab": String(index)
|
|
93
|
-
},
|
|
94
|
-
children: tab.content
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
return {
|
|
98
|
-
type: "element",
|
|
99
|
-
tagName: "div",
|
|
100
|
-
properties: {
|
|
101
|
-
className: ["ox-tabs"],
|
|
102
|
-
"data-group": groupId
|
|
103
|
-
},
|
|
104
|
-
children
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Create fallback HTML using <details> elements.
|
|
109
|
-
*/
|
|
110
|
-
function createFallbackElement(tabs) {
|
|
111
|
-
const children = [];
|
|
112
|
-
tabs.forEach((tab, index) => {
|
|
113
|
-
children.push({
|
|
114
|
-
type: "element",
|
|
115
|
-
tagName: "details",
|
|
116
|
-
properties: { open: index === 0 ? true : void 0 },
|
|
117
|
-
children: [{
|
|
118
|
-
type: "element",
|
|
119
|
-
tagName: "summary",
|
|
120
|
-
properties: {},
|
|
121
|
-
children: [{
|
|
122
|
-
type: "text",
|
|
123
|
-
value: tab.label
|
|
124
|
-
}]
|
|
125
|
-
}, {
|
|
126
|
-
type: "element",
|
|
127
|
-
tagName: "div",
|
|
128
|
-
properties: { className: ["ox-tabs-fallback-content"] },
|
|
129
|
-
children: tab.content
|
|
130
|
-
}]
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
return {
|
|
134
|
-
type: "element",
|
|
135
|
-
tagName: "noscript",
|
|
136
|
-
properties: {},
|
|
137
|
-
children: [{
|
|
138
|
-
type: "element",
|
|
139
|
-
tagName: "div",
|
|
140
|
-
properties: { className: ["ox-tabs-fallback"] },
|
|
141
|
-
children
|
|
142
|
-
}]
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Rehype plugin to transform Tabs components.
|
|
147
|
-
*/
|
|
148
|
-
function rehypeTabs() {
|
|
149
|
-
return (tree) => {
|
|
150
|
-
const visit = (node) => {
|
|
151
|
-
if ("children" in node) for (let i = 0; i < node.children.length; i++) {
|
|
152
|
-
const child = node.children[i];
|
|
153
|
-
if (child.type === "element") if (child.tagName.toLowerCase() === "tabs") {
|
|
154
|
-
const tabs = parseTabChildren(child.children);
|
|
155
|
-
if (tabs.length > 0) {
|
|
156
|
-
const wrapper = {
|
|
157
|
-
type: "element",
|
|
158
|
-
tagName: "div",
|
|
159
|
-
properties: { className: ["ox-tabs-container"] },
|
|
160
|
-
children: [createTabsElement(tabs, String(tabGroupCounter++)), createFallbackElement(tabs)]
|
|
161
|
-
};
|
|
162
|
-
node.children[i] = wrapper;
|
|
163
|
-
}
|
|
164
|
-
} else visit(child);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
visit(tree);
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
29
|
* Transform Tabs components in HTML.
|
|
172
30
|
*/
|
|
173
31
|
async function transformTabs(html) {
|
|
174
|
-
|
|
175
|
-
|
|
32
|
+
if (!/<tabs/i.test(html)) return html;
|
|
33
|
+
const result = (await require_napi.importNapiModule()).transformTabsEmbeds(html, tabGroupCounter);
|
|
34
|
+
tabGroupCounter += result.groupCount;
|
|
35
|
+
return result.html;
|
|
176
36
|
}
|
|
177
37
|
/**
|
|
178
38
|
* Generate dynamic CSS for :has() based tab switching.
|
package/dist/tabs.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs.cjs","names":["
|
|
1
|
+
{"version":3,"file":"tabs.cjs","names":["importNapiModule"],"sources":["../src/plugins/tabs.ts"],"sourcesContent":["/**\n * Tabs Plugin - Pure CSS implementation\n *\n * Transforms <Tabs>/<Tab> components into accessible HTML with CSS :has()\n * based tab switching (no JavaScript required).\n *\n * The HTML rewrite is performed in Rust (`transformTabsEmbeds` in\n * @ox-content/napi), replacing the previous rehype parse/stringify round-trip.\n * The per-build group counter (consumed by `generateTabsCSS`) stays here and is\n * advanced by the number of groups the Rust transform reports, so CSS\n * generation still covers exactly the groups that were emitted.\n */\n\nimport { importNapiModule } from \"../napi\";\n\nlet tabGroupCounter = 0;\n\n/**\n * Reset tab group counter (for testing and per dev-server request).\n */\nexport function resetTabGroupCounter(): void {\n tabGroupCounter = 0;\n}\n\n/**\n * Transform Tabs components in HTML.\n */\nexport async function transformTabs(html: string): Promise<string> {\n // Cheap marker check: skip the NAPI call entirely when there's no `<tabs>`\n // element. The Rust side guards the same way, but short-circuiting here\n // avoids marshalling the whole document across the boundary.\n if (!/<tabs/i.test(html)) {\n return html;\n }\n\n const mod = await importNapiModule();\n const result = mod.transformTabsEmbeds(html, tabGroupCounter);\n tabGroupCounter += result.groupCount;\n return result.html;\n}\n\n/**\n * Generate dynamic CSS for :has() based tab switching.\n * This is needed because :has() selectors need unique IDs.\n */\nexport function generateTabsCSS(groupCount: number): string {\n if (groupCount === 0) return \"\";\n\n let css = \"/* Dynamic Tabs CSS */\\n\";\n\n for (let g = 0; g < groupCount; g++) {\n for (let t = 0; t < 8; t++) {\n css += `.ox-tabs[data-group=\"${g}\"]:has(#ox-tab-${g}-${t}:checked) .ox-tab-panel[data-tab=\"${t}\"] { display: block; }\\n`;\n }\n }\n\n return css;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAeA,IAAI,kBAAkB;;;;AAKtB,SAAgB,uBAA6B;CAC3C,kBAAkB;;;;;AAMpB,eAAsB,cAAc,MAA+B;CAIjE,IAAI,CAAC,SAAS,KAAK,KAAK,EACtB,OAAO;CAIT,MAAM,UAAS,MADGA,aAAAA,kBAAkB,EACjB,oBAAoB,MAAM,gBAAgB;CAC7D,mBAAmB,OAAO;CAC1B,OAAO,OAAO;;;;;;AAOhB,SAAgB,gBAAgB,YAA4B;CAC1D,IAAI,eAAe,GAAG,OAAO;CAE7B,IAAI,MAAM;CAEV,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAC9B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KACrB,OAAO,wBAAwB,EAAE,iBAAiB,EAAE,GAAG,EAAE,oCAAoC,EAAE;CAInG,OAAO"}
|
package/dist/tabs2.mjs
CHANGED
|
@@ -1,170 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import rehypeParse from "rehype-parse";
|
|
3
|
-
import rehypeStringify from "rehype-stringify";
|
|
1
|
+
import { t as importNapiModule } from "./napi.mjs";
|
|
4
2
|
//#region src/plugins/tabs.ts
|
|
5
3
|
/**
|
|
6
4
|
* Tabs Plugin - Pure CSS implementation
|
|
7
5
|
*
|
|
8
|
-
* Transforms <Tabs>/<Tab> components into accessible HTML
|
|
9
|
-
*
|
|
6
|
+
* Transforms <Tabs>/<Tab> components into accessible HTML with CSS :has()
|
|
7
|
+
* based tab switching (no JavaScript required).
|
|
8
|
+
*
|
|
9
|
+
* The HTML rewrite is performed in Rust (`transformTabsEmbeds` in
|
|
10
|
+
* @ox-content/napi), replacing the previous rehype parse/stringify round-trip.
|
|
11
|
+
* The per-build group counter (consumed by `generateTabsCSS`) stays here and is
|
|
12
|
+
* advanced by the number of groups the Rust transform reports, so CSS
|
|
13
|
+
* generation still covers exactly the groups that were emitted.
|
|
10
14
|
*/
|
|
11
15
|
let tabGroupCounter = 0;
|
|
12
16
|
/**
|
|
13
|
-
* Reset tab group counter (for testing).
|
|
17
|
+
* Reset tab group counter (for testing and per dev-server request).
|
|
14
18
|
*/
|
|
15
19
|
function resetTabGroupCounter() {
|
|
16
20
|
tabGroupCounter = 0;
|
|
17
21
|
}
|
|
18
22
|
/**
|
|
19
|
-
* Get element attribute value.
|
|
20
|
-
*/
|
|
21
|
-
function getAttribute(el, name) {
|
|
22
|
-
const value = el.properties?.[name];
|
|
23
|
-
if (typeof value === "string") return value;
|
|
24
|
-
if (Array.isArray(value)) return value.join(" ");
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Parse Tab elements from Tabs children.
|
|
28
|
-
*/
|
|
29
|
-
function parseTabChildren(children) {
|
|
30
|
-
const tabs = [];
|
|
31
|
-
for (const child of children) {
|
|
32
|
-
if (child.type !== "element") continue;
|
|
33
|
-
if (child.tagName.toLowerCase() === "tab") {
|
|
34
|
-
const label = getAttribute(child, "label") || `Tab ${tabs.length + 1}`;
|
|
35
|
-
tabs.push({
|
|
36
|
-
label,
|
|
37
|
-
content: child.children.filter((c) => c.type === "element" || c.type === "text")
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return tabs;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Create the HTML structure for tabs.
|
|
45
|
-
*/
|
|
46
|
-
function createTabsElement(tabs, groupId) {
|
|
47
|
-
const children = [];
|
|
48
|
-
const headerChildren = [];
|
|
49
|
-
tabs.forEach((tab, index) => {
|
|
50
|
-
const inputId = `ox-tab-${groupId}-${index}`;
|
|
51
|
-
headerChildren.push({
|
|
52
|
-
type: "element",
|
|
53
|
-
tagName: "input",
|
|
54
|
-
properties: {
|
|
55
|
-
type: "radio",
|
|
56
|
-
name: `ox-tabs-${groupId}`,
|
|
57
|
-
id: inputId,
|
|
58
|
-
checked: index === 0 ? true : void 0
|
|
59
|
-
},
|
|
60
|
-
children: []
|
|
61
|
-
});
|
|
62
|
-
headerChildren.push({
|
|
63
|
-
type: "element",
|
|
64
|
-
tagName: "label",
|
|
65
|
-
properties: { htmlFor: inputId },
|
|
66
|
-
children: [{
|
|
67
|
-
type: "text",
|
|
68
|
-
value: tab.label
|
|
69
|
-
}]
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
children.push({
|
|
73
|
-
type: "element",
|
|
74
|
-
tagName: "div",
|
|
75
|
-
properties: { className: ["ox-tabs-header"] },
|
|
76
|
-
children: headerChildren
|
|
77
|
-
});
|
|
78
|
-
tabs.forEach((tab, index) => {
|
|
79
|
-
children.push({
|
|
80
|
-
type: "element",
|
|
81
|
-
tagName: "div",
|
|
82
|
-
properties: {
|
|
83
|
-
className: ["ox-tab-panel"],
|
|
84
|
-
"data-tab": String(index)
|
|
85
|
-
},
|
|
86
|
-
children: tab.content
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
return {
|
|
90
|
-
type: "element",
|
|
91
|
-
tagName: "div",
|
|
92
|
-
properties: {
|
|
93
|
-
className: ["ox-tabs"],
|
|
94
|
-
"data-group": groupId
|
|
95
|
-
},
|
|
96
|
-
children
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Create fallback HTML using <details> elements.
|
|
101
|
-
*/
|
|
102
|
-
function createFallbackElement(tabs) {
|
|
103
|
-
const children = [];
|
|
104
|
-
tabs.forEach((tab, index) => {
|
|
105
|
-
children.push({
|
|
106
|
-
type: "element",
|
|
107
|
-
tagName: "details",
|
|
108
|
-
properties: { open: index === 0 ? true : void 0 },
|
|
109
|
-
children: [{
|
|
110
|
-
type: "element",
|
|
111
|
-
tagName: "summary",
|
|
112
|
-
properties: {},
|
|
113
|
-
children: [{
|
|
114
|
-
type: "text",
|
|
115
|
-
value: tab.label
|
|
116
|
-
}]
|
|
117
|
-
}, {
|
|
118
|
-
type: "element",
|
|
119
|
-
tagName: "div",
|
|
120
|
-
properties: { className: ["ox-tabs-fallback-content"] },
|
|
121
|
-
children: tab.content
|
|
122
|
-
}]
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
return {
|
|
126
|
-
type: "element",
|
|
127
|
-
tagName: "noscript",
|
|
128
|
-
properties: {},
|
|
129
|
-
children: [{
|
|
130
|
-
type: "element",
|
|
131
|
-
tagName: "div",
|
|
132
|
-
properties: { className: ["ox-tabs-fallback"] },
|
|
133
|
-
children
|
|
134
|
-
}]
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Rehype plugin to transform Tabs components.
|
|
139
|
-
*/
|
|
140
|
-
function rehypeTabs() {
|
|
141
|
-
return (tree) => {
|
|
142
|
-
const visit = (node) => {
|
|
143
|
-
if ("children" in node) for (let i = 0; i < node.children.length; i++) {
|
|
144
|
-
const child = node.children[i];
|
|
145
|
-
if (child.type === "element") if (child.tagName.toLowerCase() === "tabs") {
|
|
146
|
-
const tabs = parseTabChildren(child.children);
|
|
147
|
-
if (tabs.length > 0) {
|
|
148
|
-
const wrapper = {
|
|
149
|
-
type: "element",
|
|
150
|
-
tagName: "div",
|
|
151
|
-
properties: { className: ["ox-tabs-container"] },
|
|
152
|
-
children: [createTabsElement(tabs, String(tabGroupCounter++)), createFallbackElement(tabs)]
|
|
153
|
-
};
|
|
154
|
-
node.children[i] = wrapper;
|
|
155
|
-
}
|
|
156
|
-
} else visit(child);
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
visit(tree);
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
23
|
* Transform Tabs components in HTML.
|
|
164
24
|
*/
|
|
165
25
|
async function transformTabs(html) {
|
|
166
|
-
|
|
167
|
-
|
|
26
|
+
if (!/<tabs/i.test(html)) return html;
|
|
27
|
+
const result = (await importNapiModule()).transformTabsEmbeds(html, tabGroupCounter);
|
|
28
|
+
tabGroupCounter += result.groupCount;
|
|
29
|
+
return result.html;
|
|
168
30
|
}
|
|
169
31
|
/**
|
|
170
32
|
* Generate dynamic CSS for :has() based tab switching.
|
package/dist/tabs2.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs2.mjs","names":[],"sources":["../src/plugins/tabs.ts"],"sourcesContent":["/**\n * Tabs Plugin - Pure CSS implementation\n *\n * Transforms <Tabs>/<Tab> components into accessible HTML
|
|
1
|
+
{"version":3,"file":"tabs2.mjs","names":[],"sources":["../src/plugins/tabs.ts"],"sourcesContent":["/**\n * Tabs Plugin - Pure CSS implementation\n *\n * Transforms <Tabs>/<Tab> components into accessible HTML with CSS :has()\n * based tab switching (no JavaScript required).\n *\n * The HTML rewrite is performed in Rust (`transformTabsEmbeds` in\n * @ox-content/napi), replacing the previous rehype parse/stringify round-trip.\n * The per-build group counter (consumed by `generateTabsCSS`) stays here and is\n * advanced by the number of groups the Rust transform reports, so CSS\n * generation still covers exactly the groups that were emitted.\n */\n\nimport { importNapiModule } from \"../napi\";\n\nlet tabGroupCounter = 0;\n\n/**\n * Reset tab group counter (for testing and per dev-server request).\n */\nexport function resetTabGroupCounter(): void {\n tabGroupCounter = 0;\n}\n\n/**\n * Transform Tabs components in HTML.\n */\nexport async function transformTabs(html: string): Promise<string> {\n // Cheap marker check: skip the NAPI call entirely when there's no `<tabs>`\n // element. The Rust side guards the same way, but short-circuiting here\n // avoids marshalling the whole document across the boundary.\n if (!/<tabs/i.test(html)) {\n return html;\n }\n\n const mod = await importNapiModule();\n const result = mod.transformTabsEmbeds(html, tabGroupCounter);\n tabGroupCounter += result.groupCount;\n return result.html;\n}\n\n/**\n * Generate dynamic CSS for :has() based tab switching.\n * This is needed because :has() selectors need unique IDs.\n */\nexport function generateTabsCSS(groupCount: number): string {\n if (groupCount === 0) return \"\";\n\n let css = \"/* Dynamic Tabs CSS */\\n\";\n\n for (let g = 0; g < groupCount; g++) {\n for (let t = 0; t < 8; t++) {\n css += `.ox-tabs[data-group=\"${g}\"]:has(#ox-tab-${g}-${t}:checked) .ox-tab-panel[data-tab=\"${t}\"] { display: block; }\\n`;\n }\n }\n\n return css;\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,IAAI,kBAAkB;;;;AAKtB,SAAgB,uBAA6B;CAC3C,kBAAkB;;;;;AAMpB,eAAsB,cAAc,MAA+B;CAIjE,IAAI,CAAC,SAAS,KAAK,KAAK,EACtB,OAAO;CAIT,MAAM,UAAS,MADG,kBAAkB,EACjB,oBAAoB,MAAM,gBAAgB;CAC7D,mBAAmB,OAAO;CAC1B,OAAO,OAAO;;;;;;AAOhB,SAAgB,gBAAgB,YAA4B;CAC1D,IAAI,eAAe,GAAG,OAAO;CAE7B,IAAI,MAAM;CAEV,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAC9B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KACrB,OAAO,wBAAwB,EAAE,iBAAiB,EAAE,GAAG,EAAE,oCAAoC,EAAE;CAInG,OAAO"}
|
package/dist/youtube.cjs
CHANGED
|
@@ -1,34 +1,21 @@
|
|
|
1
1
|
const require_chunk = require("./chunk.cjs");
|
|
2
|
-
|
|
3
|
-
let rehype_parse = require("rehype-parse");
|
|
4
|
-
rehype_parse = require_chunk.__toESM(rehype_parse, 1);
|
|
5
|
-
let rehype_stringify = require("rehype-stringify");
|
|
6
|
-
rehype_stringify = require_chunk.__toESM(rehype_stringify, 1);
|
|
2
|
+
const require_napi = require("./napi.cjs");
|
|
7
3
|
//#region src/plugins/youtube.ts
|
|
8
4
|
/**
|
|
9
5
|
* YouTube Plugin - Privacy-enhanced iframe embedding
|
|
10
6
|
*
|
|
11
|
-
* Transforms <YouTube> components into responsive iframe embeds
|
|
12
|
-
*
|
|
7
|
+
* Transforms <YouTube> components into responsive iframe embeds using
|
|
8
|
+
* youtube-nocookie.com for enhanced privacy.
|
|
9
|
+
*
|
|
10
|
+
* The HTML rewrite is performed in Rust (`transformYoutubeEmbeds` in
|
|
11
|
+
* @ox-content/napi), replacing the previous rehype parse/stringify
|
|
12
|
+
* round-trip. This module keeps the public TS surface and a cheap marker
|
|
13
|
+
* check so pages without a `<youtube>` element never cross the NAPI boundary.
|
|
13
14
|
*/
|
|
14
15
|
var youtube_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
15
16
|
extractVideoId: () => extractVideoId,
|
|
16
17
|
transformYouTube: () => transformYouTube
|
|
17
18
|
});
|
|
18
|
-
const defaultOptions = {
|
|
19
|
-
privacyEnhanced: true,
|
|
20
|
-
aspectRatio: "16/9",
|
|
21
|
-
allowFullscreen: true,
|
|
22
|
-
lazyLoad: true
|
|
23
|
-
};
|
|
24
|
-
/**
|
|
25
|
-
* Get element attribute value.
|
|
26
|
-
*/
|
|
27
|
-
function getAttribute(el, name) {
|
|
28
|
-
const value = el.properties?.[name];
|
|
29
|
-
if (typeof value === "string") return value;
|
|
30
|
-
if (Array.isArray(value)) return value.join(" ");
|
|
31
|
-
}
|
|
32
19
|
/**
|
|
33
20
|
* Extract YouTube video ID from various URL formats.
|
|
34
21
|
*/
|
|
@@ -41,77 +28,11 @@ function extractVideoId(input) {
|
|
|
41
28
|
return null;
|
|
42
29
|
}
|
|
43
30
|
/**
|
|
44
|
-
* Build YouTube embed URL with parameters.
|
|
45
|
-
*/
|
|
46
|
-
function buildEmbedUrl(videoId, options, params) {
|
|
47
|
-
const domain = options.privacyEnhanced ? "www.youtube-nocookie.com" : "www.youtube.com";
|
|
48
|
-
const url = new URL(`https://${domain}/embed/${videoId}`);
|
|
49
|
-
if (params) for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value);
|
|
50
|
-
return url.toString();
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Create YouTube embed element.
|
|
54
|
-
*/
|
|
55
|
-
function createYouTubeElement(videoId, options, title, start) {
|
|
56
|
-
const params = {};
|
|
57
|
-
if (start) params.start = start;
|
|
58
|
-
const iframe = {
|
|
59
|
-
type: "element",
|
|
60
|
-
tagName: "iframe",
|
|
61
|
-
properties: {
|
|
62
|
-
src: buildEmbedUrl(videoId, options, params),
|
|
63
|
-
title: title || `YouTube video ${videoId}`,
|
|
64
|
-
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
|
|
65
|
-
referrerpolicy: "strict-origin-when-cross-origin",
|
|
66
|
-
allowfullscreen: options.allowFullscreen || void 0,
|
|
67
|
-
loading: options.lazyLoad ? "lazy" : void 0
|
|
68
|
-
},
|
|
69
|
-
children: []
|
|
70
|
-
};
|
|
71
|
-
return {
|
|
72
|
-
type: "element",
|
|
73
|
-
tagName: "div",
|
|
74
|
-
properties: {
|
|
75
|
-
className: ["ox-youtube"],
|
|
76
|
-
style: `aspect-ratio: ${options.aspectRatio};`
|
|
77
|
-
},
|
|
78
|
-
children: [iframe]
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Rehype plugin to transform YouTube components.
|
|
83
|
-
*/
|
|
84
|
-
function rehypeYouTube(options) {
|
|
85
|
-
return (tree) => {
|
|
86
|
-
const visit = (node) => {
|
|
87
|
-
if ("children" in node) for (let i = 0; i < node.children.length; i++) {
|
|
88
|
-
const child = node.children[i];
|
|
89
|
-
if (child.type === "element") if (child.tagName.toLowerCase() === "youtube") {
|
|
90
|
-
const id = getAttribute(child, "id");
|
|
91
|
-
const url = getAttribute(child, "url");
|
|
92
|
-
const title = getAttribute(child, "title");
|
|
93
|
-
const start = getAttribute(child, "start");
|
|
94
|
-
const videoId = id ? extractVideoId(id) : url ? extractVideoId(url) : null;
|
|
95
|
-
if (videoId) {
|
|
96
|
-
const youtubeElement = createYouTubeElement(videoId, options, title, start);
|
|
97
|
-
node.children[i] = youtubeElement;
|
|
98
|
-
}
|
|
99
|
-
} else visit(child);
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
visit(tree);
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
31
|
* Transform YouTube components in HTML.
|
|
107
32
|
*/
|
|
108
33
|
async function transformYouTube(html, options) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
...options
|
|
112
|
-
};
|
|
113
|
-
const result = await (0, unified.unified)().use(rehype_parse.default, { fragment: true }).use(rehypeYouTube, mergedOptions).use(rehype_stringify.default).process(html);
|
|
114
|
-
return String(result);
|
|
34
|
+
if (!/<youtube/i.test(html)) return html;
|
|
35
|
+
return (await require_napi.importNapiModule()).transformYoutubeEmbeds(html, options);
|
|
115
36
|
}
|
|
116
37
|
//#endregion
|
|
117
38
|
Object.defineProperty(exports, "extractVideoId", {
|
package/dist/youtube.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"youtube.cjs","names":["
|
|
1
|
+
{"version":3,"file":"youtube.cjs","names":["importNapiModule"],"sources":["../src/plugins/youtube.ts"],"sourcesContent":["/**\n * YouTube Plugin - Privacy-enhanced iframe embedding\n *\n * Transforms <YouTube> components into responsive iframe embeds using\n * youtube-nocookie.com for enhanced privacy.\n *\n * The HTML rewrite is performed in Rust (`transformYoutubeEmbeds` in\n * @ox-content/napi), replacing the previous rehype parse/stringify\n * round-trip. This module keeps the public TS surface and a cheap marker\n * check so pages without a `<youtube>` element never cross the NAPI boundary.\n */\n\nimport { importNapiModule } from \"../napi\";\n\nexport interface YouTubeOptions {\n /** Use privacy-enhanced mode (youtube-nocookie.com). Default: true */\n privacyEnhanced?: boolean;\n /** Default aspect ratio. Default: \"16/9\" */\n aspectRatio?: string;\n /** Allow fullscreen. Default: true */\n allowFullscreen?: boolean;\n /** Lazy load iframe. Default: true */\n lazyLoad?: boolean;\n}\n\n/**\n * Extract YouTube video ID from various URL formats.\n */\nexport function extractVideoId(input: string): string | null {\n // Already a video ID (11 characters, alphanumeric + _ -)\n if (/^[a-zA-Z0-9_-]{11}$/.test(input)) {\n return input;\n }\n\n // Full URL patterns\n const patterns = [\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/|youtube\\.com\\/v\\/)([a-zA-Z0-9_-]{11})/,\n /youtube\\.com\\/shorts\\/([a-zA-Z0-9_-]{11})/,\n ];\n\n for (const pattern of patterns) {\n const match = input.match(pattern);\n if (match) return match[1];\n }\n\n return null;\n}\n\n/**\n * Transform YouTube components in HTML.\n */\nexport async function transformYouTube(html: string, options?: YouTubeOptions): Promise<string> {\n // Cheap marker check: skip the NAPI call entirely when there's no\n // `<youtube>` element (the common case). The Rust side guards the same way,\n // but short-circuiting here avoids marshalling the whole document across\n // the boundary.\n if (!/<youtube/i.test(html)) {\n return html;\n }\n\n const mod = await importNapiModule();\n return mod.transformYoutubeEmbeds(html, options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,eAAe,OAA8B;CAE3D,IAAI,sBAAsB,KAAK,MAAM,EACnC,OAAO;CAST,KAAK,MAAM,WAAW,CAJpB,sGACA,4CAG4B,EAAE;EAC9B,MAAM,QAAQ,MAAM,MAAM,QAAQ;EAClC,IAAI,OAAO,OAAO,MAAM;;CAG1B,OAAO;;;;;AAMT,eAAsB,iBAAiB,MAAc,SAA2C;CAK9F,IAAI,CAAC,YAAY,KAAK,KAAK,EACzB,OAAO;CAIT,QAAO,MADWA,aAAAA,kBAAkB,EACzB,uBAAuB,MAAM,QAAQ"}
|