@daydreamui/html-paste 0.0.0-stage → 0.5.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.js ADDED
@@ -0,0 +1,278 @@
1
+ import { flush, untrack } from "solid-js";
2
+ var BLOCK_TAGS = new Set([
3
+ ...[
4
+ "html",
5
+ "body",
6
+ "address",
7
+ "article",
8
+ "aside",
9
+ "blockquote"
10
+ ],
11
+ ...[
12
+ "caption",
13
+ "center",
14
+ "col",
15
+ "colgroup",
16
+ "dd",
17
+ "details",
18
+ "dialog"
19
+ ],
20
+ ...[
21
+ "dir",
22
+ "div",
23
+ "dl",
24
+ "dt",
25
+ "fieldset",
26
+ "figcaption",
27
+ "figure"
28
+ ],
29
+ ...[
30
+ "footer",
31
+ "form",
32
+ "h1",
33
+ "h2",
34
+ "h3",
35
+ "h4",
36
+ "h5",
37
+ "h6",
38
+ "header"
39
+ ],
40
+ ...[
41
+ "hgroup",
42
+ "hr",
43
+ "legend",
44
+ "li",
45
+ "main",
46
+ "menu",
47
+ "nav",
48
+ "ol"
49
+ ],
50
+ ...[
51
+ "optgroup",
52
+ "option",
53
+ "p",
54
+ "pre",
55
+ "search",
56
+ "section",
57
+ "summary"
58
+ ],
59
+ ...[
60
+ "table",
61
+ "tbody",
62
+ "td",
63
+ "tfoot",
64
+ "th",
65
+ "thead",
66
+ "tr",
67
+ "ul"
68
+ ]
69
+ ]);
70
+ var UNSHOWN_TAGS = new Set([...[
71
+ "head",
72
+ "meta",
73
+ "link",
74
+ "title",
75
+ "base",
76
+ "style"
77
+ ], ...[
78
+ "script",
79
+ "noscript",
80
+ "template",
81
+ "object",
82
+ "embed"
83
+ ]]);
84
+ var REPLACED_TAGS = new Set([...[
85
+ "img",
86
+ "picture",
87
+ "video",
88
+ "audio",
89
+ "svg",
90
+ "iframe"
91
+ ]]);
92
+ var WRAPPER_TAGS = new Set([
93
+ ...[
94
+ "p",
95
+ "h1",
96
+ "h2",
97
+ "h3",
98
+ "h4",
99
+ "h5",
100
+ "h6",
101
+ "pre",
102
+ "address",
103
+ "div"
104
+ ],
105
+ ...[
106
+ "section",
107
+ "article",
108
+ "main",
109
+ "header",
110
+ "footer",
111
+ "aside",
112
+ "nav"
113
+ ],
114
+ ...["blockquote", "center"]
115
+ ]);
116
+ function isSingleParagraph(doc) {
117
+ let scope = doc.body;
118
+ for (;;) {
119
+ const children = contentChildren(scope);
120
+ const elements = children.filter((node) => node.nodeType === Node.ELEMENT_NODE);
121
+ const hasText = children.some((node) => node.nodeType === Node.TEXT_NODE);
122
+ const only = elements[0];
123
+ if (elements.length === 1 && !hasText && only !== void 0) {
124
+ const tag = only.localName;
125
+ if (WRAPPER_TAGS.has(tag) || inline(tag) && !REPLACED_TAGS.has(tag)) {
126
+ scope = only;
127
+ continue;
128
+ }
129
+ }
130
+ return elements.every(isPhrasingOnly);
131
+ }
132
+ }
133
+ function hasContent(doc) {
134
+ if ((doc.body.textContent ?? "").trim() !== "") return true;
135
+ return Array.from(doc.body.querySelectorAll("*")).some((element) => REPLACED_TAGS.has(element.localName));
136
+ }
137
+ function inline(tag) {
138
+ return !BLOCK_TAGS.has(tag);
139
+ }
140
+ function contentChildren(scope) {
141
+ return Array.from(scope.childNodes).filter((node) => {
142
+ if (node.nodeType === Node.TEXT_NODE) return node.data.trim() !== "";
143
+ if (node.nodeType !== Node.ELEMENT_NODE) return false;
144
+ return !UNSHOWN_TAGS.has(node.localName);
145
+ });
146
+ }
147
+ function isPhrasingOnly(element) {
148
+ const tag = element.localName;
149
+ if (UNSHOWN_TAGS.has(tag)) return true;
150
+ if (!inline(tag) || REPLACED_TAGS.has(tag)) return false;
151
+ return Array.from(element.children).every(isPhrasingOnly);
152
+ }
153
+ var MAX_ELEMENTS = 1e4;
154
+ var MAX_SOURCE = 4e6;
155
+ function parseHtml(html) {
156
+ return new DOMParser().parseFromString(withoutBom(html), "text/html");
157
+ }
158
+ var BOM = "";
159
+ function withoutBom(text) {
160
+ return text.startsWith(BOM) ? text.slice(1) : text;
161
+ }
162
+ function pastedElements(doc) {
163
+ const count = (root) => {
164
+ let n = 0;
165
+ for (const el of root.querySelectorAll("*")) {
166
+ n += 1;
167
+ if (el.localName === "template" && "content" in el) n += count(el.content);
168
+ }
169
+ return n;
170
+ };
171
+ return count(doc.head) + count(doc.body);
172
+ }
173
+ function hasElements(doc) {
174
+ return doc.body.firstElementChild !== null;
175
+ }
176
+ var DOCTYPE = "<!doctype html>\n";
177
+ function pageFile(source) {
178
+ if (source.doc.doctype !== null) return source.text;
179
+ const rest = withoutBom(source.text);
180
+ return source.text.slice(0, source.text.length - rest.length) + DOCTYPE + rest;
181
+ }
182
+ function looksLikeMarkup(text) {
183
+ return text.trimStart().startsWith("<") && /<\/[a-z][\w:-]*\s*>/i.test(text);
184
+ }
185
+ function htmlSource(transfer) {
186
+ if (transfer === null || transfer.files.length > 0) return null;
187
+ const text = transfer.getData("text/plain");
188
+ if (looksLikeMarkup(text)) {
189
+ const doc = parseHtml(text);
190
+ if (hasElements(doc) && hasContent(doc)) return {
191
+ text,
192
+ doc
193
+ };
194
+ }
195
+ const html = transfer.getData("text/html");
196
+ if (html === "") return null;
197
+ const doc = parseHtml(html);
198
+ if (!hasElements(doc) || !hasContent(doc)) return null;
199
+ if (isSingleParagraph(doc)) return null;
200
+ return {
201
+ text: html,
202
+ doc
203
+ };
204
+ }
205
+ function registerHtmlPaste(dd) {
206
+ let consecutivePastes = 0;
207
+ let pasting = 0;
208
+ let camera = untrack(() => dd.geometry.camera());
209
+ const startOver = () => {
210
+ consecutivePastes = 0;
211
+ };
212
+ const reset = () => {
213
+ if (pasting === 0) startOver();
214
+ };
215
+ dd.canvas.onActivity(startOver);
216
+ dd.on("document", reset);
217
+ dd.on("selection", reset);
218
+ dd.on("geometry", () => {
219
+ const next = untrack(() => dd.geometry.camera());
220
+ if (next.panX !== camera.panX || next.panY !== camera.panY || next.zoom !== camera.zoom) startOver();
221
+ camera = next;
222
+ });
223
+ const why = (error) => error instanceof Error ? error.message : String(error);
224
+ const land = async (text, position) => {
225
+ pasting += 1;
226
+ try {
227
+ let made;
228
+ try {
229
+ made = await dd.createPage(text, {
230
+ position,
231
+ frame: { width: 960 }
232
+ });
233
+ } catch (error) {
234
+ console.error(`[${dd.plugin.id}] paste refused: ${why(error)}`);
235
+ return;
236
+ }
237
+ try {
238
+ dd.select(made.viewportId);
239
+ flush();
240
+ } catch (error) {
241
+ console.error(`[${dd.plugin.id}] pasted a page: ${made.path}, but its viewport could not be selected: ${why(error)}`);
242
+ return;
243
+ }
244
+ console.info(`[${dd.plugin.id}] pasted a page: ${made.path}, 960px wide`);
245
+ for (const { url, reason } of made.unvendored) console.warn(`[${dd.plugin.id}] ${url} could not be downloaded into assets/ (${reason}): it stays as written`);
246
+ } finally {
247
+ pasting -= 1;
248
+ }
249
+ };
250
+ dd.canvas.onPaste((event) => {
251
+ const transfer = event.clipboardData;
252
+ const bytes = Math.max(transfer?.getData("text/html").length ?? 0, transfer?.getData("text/plain").length ?? 0);
253
+ if (bytes > 4e6) {
254
+ event.preventDefault();
255
+ console.error(`[${dd.plugin.id}] paste refused: ${bytes} characters; a paste carries at most ${MAX_SOURCE}`);
256
+ return;
257
+ }
258
+ const source = htmlSource(transfer);
259
+ if (source === null) return;
260
+ event.preventDefault();
261
+ const size = pastedElements(source.doc);
262
+ if (size > 1e4) {
263
+ console.error(`[${dd.plugin.id}] paste refused: ${size} elements; a viewport holds at most ${MAX_ELEMENTS}`);
264
+ return;
265
+ }
266
+ const cascade = consecutivePastes++ * 16;
267
+ const center = dd.canvas.center();
268
+ const position = {
269
+ x: center.x - 480 + cascade,
270
+ y: center.y - 240 + cascade
271
+ };
272
+ land(pageFile(source), position);
273
+ }, { priority: 5 });
274
+ }
275
+ function activate(dd) {
276
+ registerHtmlPaste(dd);
277
+ }
278
+ export { activate as default };
package/icon.svg ADDED
@@ -0,0 +1,5 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="1.8" stroke-linecap="square" stroke-linejoin="miter">
2
+ <path d="M6.5 5H4v16h16V5h-2.5" stroke="#839999"/>
3
+ <path d="M8 2h8v4H8V2Z" fill="#F66A2C"/>
4
+ <path d="m9.5 10-3 3 3 3m5-6 3 3-3 3" stroke="#E7E5DE"/>
5
+ </svg>
package/manifest.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "id": "daydream.html-paste",
3
+ "name": "HTML paste",
4
+ "description": "Paste HTML on the canvas and it becomes a new page of the project.",
5
+ "version": "0.5.0",
6
+ "api": "3.1",
7
+ "contributes": {
8
+ "instructions": "Pasting HTML on the canvas (Cmd+V with markup as plain text, or text/html that is more than a single paragraph of prose; a paragraph copied from a page lands as canvas text through the Text plugin instead) makes a new page of the project: the pasted text is written as a new html file, as it arrived \u2014 its <style> and <link> kept, nothing cleaned or folded, a fragment written after <!doctype html> so a browser renders the file as the canvas does \u2014 named index.html in a project with no page yet, else after its <title> made file-safe (page.html with none; -2 and on while the name is taken), at the project's root. Each remote image, video, audio, text track and font file it names is downloaded into assets/ and named there; one that cannot be stays as written, and the console says which and why. A viewport of the page, 960px wide, is placed at the view's center and selected, as one undo step. A paste with no project open is refused, in Daydream's words, and one of more than 10,000 elements or four million characters is refused before anything is written. No gates run on a paste; lint judges the page as it judges every page."
9
+ },
10
+ "built": "dist/index.js"
11
+ }
package/package.json CHANGED
@@ -1,6 +1,17 @@
1
1
  {
2
2
  "name": "@daydreamui/html-paste",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.5.0",
4
+ "type": "module",
5
+ "description": "Paste HTML on the canvas and it becomes a new page of the project.",
6
+ "keywords": [
7
+ "daydream-plugin"
8
+ ],
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/martinbavio/daydream-plugins.git",
15
+ "directory": "plugins/daydream.html-paste"
16
+ }
17
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.