geometryview 0.1.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -15
- package/package.json +5 -4
- package/src/mcp.mjs +1 -1
- package/src/native.mjs +147 -15
- package/src/tools.mjs +135 -5
- package/web/NOTICES.md +4 -0
- package/web/build/geometryview-thumbs-web.mjs +2 -0
- package/web/build/geometryview-thumbs-web.wasm +0 -0
- package/web/build/geometryview-web.mjs +1 -1
- package/web/build/geometryview-web.wasm +0 -0
- package/web/engine.js +4 -0
- package/web/gallery-core.js +359 -0
- package/web/gallery-library.js +262 -0
- package/web/gallery-toolbar.js +148 -0
- package/web/gallery-view.js +1018 -0
- package/web/gallery.css +94 -0
- package/web/gallery.js +563 -0
- package/web/icons.js +6 -0
- package/web/import-queue.js +168 -0
- package/web/import-worker.js +63 -9
- package/web/importers.js +140 -0
- package/web/index.html +1 -0
- package/web/kept.js +263 -0
- package/web/library-store.js +116 -3
- package/web/library.js +25 -1
- package/web/occt/geometryview-step-web.wasm +0 -0
- package/web/occt-threads/geometryview-step-web.wasm +0 -0
- package/web/pdf.js +54 -0
- package/web/thumbs-worker.js +86 -0
- package/web/thumbs.js +97 -0
- package/web/viewer.js +726 -25
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// The gallery's toolbar, in place of the viewer's while the gallery shows: the view switch, the
|
|
2
|
+
// view's own controls (Sheet's scale, picture size and order), and the Look pill. The page's
|
|
3
|
+
// counterpart of the Mac's gallery toolbar (App/Sources/Gallery/GalleryToolbar.swift).
|
|
4
|
+
//
|
|
5
|
+
// Browsers keep ⌘1–⌘5 for their tabs, so the views are on ⌥1–⌥5 here.
|
|
6
|
+
|
|
7
|
+
import { Styles } from "./chrome.js";
|
|
8
|
+
import { ViewNames } from "./gallery-core.js";
|
|
9
|
+
import { iconNode, icons } from "./icons.js";
|
|
10
|
+
import { el, pill, segmented } from "./ui.js";
|
|
11
|
+
|
|
12
|
+
const kViewIcons = ["plates", "sheet", "ladders", "atlas", "catalogue"];
|
|
13
|
+
const label = (text) => el("span.tlabel", { text });
|
|
14
|
+
|
|
15
|
+
export class GalleryToolbar {
|
|
16
|
+
element;
|
|
17
|
+
#buttons = [];
|
|
18
|
+
#sheet;
|
|
19
|
+
#scale;
|
|
20
|
+
#size;
|
|
21
|
+
#sort;
|
|
22
|
+
#look;
|
|
23
|
+
#poster;
|
|
24
|
+
#ladders;
|
|
25
|
+
#laddersSort;
|
|
26
|
+
#laddersScale;
|
|
27
|
+
#atlas;
|
|
28
|
+
#atlasLevels;
|
|
29
|
+
#catalogue;
|
|
30
|
+
#pages;
|
|
31
|
+
#contents;
|
|
32
|
+
#index;
|
|
33
|
+
/** While a poster shows: Save PNG, Save PDF and Done, over its top margin. */
|
|
34
|
+
posterBar;
|
|
35
|
+
#savePDF;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* `on` takes view(index), sheet({ scale, cell, sort }) with what changed, look(), poster() to show
|
|
39
|
+
* or put away the poster, and save() for its PNG.
|
|
40
|
+
*/
|
|
41
|
+
constructor(on) {
|
|
42
|
+
const views = el("div.segmented.views", { role: "tablist", "aria-label": "View" });
|
|
43
|
+
ViewNames.forEach((name, view) => {
|
|
44
|
+
const button = el("button", { type: "button", role: "tab", title: `${name} (⌥${view + 1})`,
|
|
45
|
+
onclick: () => on.view(view) },
|
|
46
|
+
[el("span.glyph", {}, [iconNode(kViewIcons[view], 15)]), el("span.nm", { text: name }),
|
|
47
|
+
el("span.k", { text: `⌥${view + 1}` })]);
|
|
48
|
+
this.#buttons.push(button);
|
|
49
|
+
views.append(button);
|
|
50
|
+
});
|
|
51
|
+
this.#scale = segmented(["Fit", "Folder", "True"], {
|
|
52
|
+
help: "How parts are scaled",
|
|
53
|
+
tips: ["Each part fills its cell", "One scale for each folder", "One scale for everything in view"],
|
|
54
|
+
onChange: (scale) => on.sheet({ scale }),
|
|
55
|
+
});
|
|
56
|
+
this.#size = el("input.slider", { type: "range", min: 72, max: 200, step: 4, title: "Picture size",
|
|
57
|
+
"aria-label": "Picture size" });
|
|
58
|
+
this.#size.addEventListener("input", () => on.sheet({ cell: Number(this.#size.value) }));
|
|
59
|
+
this.#sort = segmented(["Name", "Size"], { help: "Order", onChange: (sort) => on.sheet({ sort }) });
|
|
60
|
+
this.#sheet = el("div.group", {}, [el("span.rule"), label("SCALE"), this.#scale.element, label("SIZE"),
|
|
61
|
+
this.#size, label("SORT"), this.#sort.element]);
|
|
62
|
+
// Ladders: the order (shared with Sheet) and the scale, each strip's own or one for all.
|
|
63
|
+
this.#laddersSort = segmented(["Size", "Name"], { help: "Order", onChange: (i) => on.sheet({ sort: i === 0 ? 1 : 0 }) });
|
|
64
|
+
this.#laddersScale = segmented(["Each", "Shared"], {
|
|
65
|
+
help: "Scale", tips: ["Each strip at its own scale", "Every strip at one scale"],
|
|
66
|
+
onChange: (i) => on.ladders?.({ shared: i === 1 }),
|
|
67
|
+
});
|
|
68
|
+
this.#ladders = el("div.group.desktop-only", {}, [el("span.rule"), label("SORT"), this.#laddersSort.element,
|
|
69
|
+
label("SCALE"), this.#laddersScale.element]);
|
|
70
|
+
// Atlas: out a level, the level, in a level.
|
|
71
|
+
this.#atlasLevels = segmented(["Regions", "Districts", "Parts"], { help: "Level", onChange: (level) => on.atlas?.({ level }) });
|
|
72
|
+
this.#atlas = el("div.group", {}, [el("span.rule"),
|
|
73
|
+
pill({ title: "\u2212", help: "Zoom out a level (\u2212)", onPress: () => on.atlas?.({ step: -1 }) }),
|
|
74
|
+
this.#atlasLevels.element,
|
|
75
|
+
pill({ title: "+", help: "Zoom in a level (+)", onPress: () => on.atlas?.({ step: 1 }) })]);
|
|
76
|
+
// Catalogue: turn the book, the pages in view, the contents and the index.
|
|
77
|
+
this.#pages = el("span.tlabel.pages", { text: "" });
|
|
78
|
+
this.#catalogue = el("div.group", {}, [el("span.rule"),
|
|
79
|
+
pill({ title: "\u2039", help: "Turn back (\u2190)", onPress: () => on.catalogue?.({ step: -1 }) }),
|
|
80
|
+
this.#pages,
|
|
81
|
+
pill({ title: "\u203a", help: "Turn on (\u2192)", onPress: () => on.catalogue?.({ step: 1 }) }),
|
|
82
|
+
this.#contents = pill({ title: "Contents", help: "The contents", onPress: () => on.catalogue?.({ spread: 1 }) }),
|
|
83
|
+
this.#index = pill({ title: "Index", help: "The index", onPress: () => on.catalogue?.({ index: true }) })]);
|
|
84
|
+
// Short of room, the view switch drops its keys, then Poster and Look their titles (.fold).
|
|
85
|
+
addEventListener("resize", () => this.#fit());
|
|
86
|
+
this.#look = pill({ icon: icons.palette, title: Styles[0].title, help: "Look", onPress: () => on.look() });
|
|
87
|
+
this.#poster = pill({ icon: icons.poster, title: "Poster", help: "Show it off: the view as a poster (⌥P)",
|
|
88
|
+
onPress: () => on.poster() });
|
|
89
|
+
this.#poster.classList.add("desktop-only", "fold");
|
|
90
|
+
this.#look.classList.add("fold");
|
|
91
|
+
this.#sheet.classList.add("desktop-only");
|
|
92
|
+
// On a phone: the Library's sheet, raised from here.
|
|
93
|
+
const library = pill({ icon: icons.parts, help: "Library", onPress: () => on.library?.() });
|
|
94
|
+
library.classList.add("phone-only");
|
|
95
|
+
this.element = el("nav.card.toolbar.gallery-toolbar", { "aria-label": "Gallery", hidden: true },
|
|
96
|
+
[library, views, this.#sheet, this.#ladders, this.#atlas, this.#catalogue, el("span.rule.desktop-only"),
|
|
97
|
+
this.#poster, this.#look]);
|
|
98
|
+
this.posterBar = el("div.card.gallery-poster-bar", { hidden: true, "aria-label": "Poster" }, [
|
|
99
|
+
pill({ title: "Save PNG", help: "Save the poster as a picture", onPress: () => on.save() }),
|
|
100
|
+
this.#savePDF = pill({ title: "Save PDF", help: "Save the poster, or in Catalogue the whole book, as a PDF",
|
|
101
|
+
onPress: () => on.savePDF() }),
|
|
102
|
+
pill({ title: "Done", help: "Back to the gallery (Esc)", onPress: () => on.poster() }),
|
|
103
|
+
]);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Save PDF's label while a PDF is made, "Page 3 of 57…", or null when done. */
|
|
107
|
+
savingPDF(text) {
|
|
108
|
+
this.#savePDF.querySelector("span").textContent = text ?? "Save PDF";
|
|
109
|
+
this.#savePDF.disabled = text != null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Shows the view in use among those enabled (a bit each), Sheet's controls, and the style. */
|
|
113
|
+
show({ view, views, sheet, style, ladders = { shared: false }, atlasLevel = 0,
|
|
114
|
+
catalogue = { spread: 0, spreads: 1, index: 0 } }) {
|
|
115
|
+
this.#catalogue.classList.toggle("off", view !== 4);
|
|
116
|
+
this.#contents.classList.toggle("on", catalogue.spread === 1);
|
|
117
|
+
this.#index.classList.toggle("on", catalogue.spread > 0 && catalogue.spread >= catalogue.index);
|
|
118
|
+
const pages = catalogue.spreads * 2;
|
|
119
|
+
this.#pages.textContent = catalogue.spread === 0 ? `Cover \u00b7 ${pages - 2} pages`
|
|
120
|
+
: `Pages ${catalogue.spread * 2}\u2013${catalogue.spread * 2 + 1} of ${pages - 1}`;
|
|
121
|
+
this.#atlas.classList.toggle("off", view !== 3);
|
|
122
|
+
this.#atlasLevels.selected = atlasLevel;
|
|
123
|
+
this.#ladders.classList.toggle("off", view !== 2);
|
|
124
|
+
this.#laddersSort.selected = sheet.sort === 1 ? 0 : 1;
|
|
125
|
+
this.#laddersScale.selected = ladders.shared ? 1 : 0;
|
|
126
|
+
this.#buttons.forEach((button, i) => {
|
|
127
|
+
button.hidden = (views & (1 << i)) === 0;
|
|
128
|
+
button.classList.toggle("on", i === view);
|
|
129
|
+
button.setAttribute("aria-selected", i === view ? "true" : "false");
|
|
130
|
+
});
|
|
131
|
+
this.#sheet.classList.toggle("off", view !== 1);
|
|
132
|
+
this.#scale.selected = sheet.scale;
|
|
133
|
+
if (Number(this.#size.value) !== sheet.cell) this.#size.value = String(sheet.cell);
|
|
134
|
+
this.#sort.selected = sheet.sort;
|
|
135
|
+
this.#look.querySelector("span").textContent = Styles[style]?.title ?? "";
|
|
136
|
+
this.#fit();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Fits the toolbar into its room, as the Mac's does: keys go first, then two titles. */
|
|
140
|
+
#fit() {
|
|
141
|
+
const bar = this.element;
|
|
142
|
+
bar.classList.remove("tight", "tighter");
|
|
143
|
+
for (const step of ["tight", "tighter"]) {
|
|
144
|
+
if (bar.scrollWidth <= bar.clientWidth) break;
|
|
145
|
+
bar.classList.add(step);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|