@zinejs/core 0.4.1 → 0.7.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 +11 -0
- package/dist/bundled-Cp43MIt0.js +100 -0
- package/dist/bundled-Cp43MIt0.js.map +1 -0
- package/dist/controls/arrows.d.ts +17 -0
- package/dist/controls/builtins.d.ts +8 -0
- package/dist/controls/controls.d.ts +14 -0
- package/dist/controls/icons.d.ts +51 -0
- package/dist/controls/outline.d.ts +14 -0
- package/dist/controls/qr.d.ts +16 -0
- package/dist/controls/registry.d.ts +21 -0
- package/dist/controls/search.d.ts +13 -0
- package/dist/controls/share.d.ts +13 -0
- package/dist/controls/sidebar.d.ts +24 -0
- package/dist/controls/socials.d.ts +16 -0
- package/dist/controls/styles.d.ts +3 -0
- package/dist/controls/thumbnails.d.ts +17 -0
- package/dist/controls/toolbar.d.ts +45 -0
- package/dist/controls/types.d.ts +81 -0
- package/dist/controls-BrPo8pKL.js +649 -0
- package/dist/controls-BrPo8pKL.js.map +1 -0
- package/dist/curls.js +133 -0
- package/dist/curls.js.map +1 -0
- package/dist/engine/deeplink.d.ts +18 -0
- package/dist/engine/emitter.d.ts +6 -0
- package/dist/engine/zoomTile.d.ts +65 -0
- package/dist/geometry/curls/bundled.d.ts +16 -0
- package/dist/geometry/curls/index.d.ts +8 -4
- package/dist/geometry/curls/types.d.ts +23 -1
- package/dist/icons-D87p7JMP.js +29 -0
- package/dist/icons-D87p7JMP.js.map +1 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.js +788 -363
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +447 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/registry-5nuYFMd4.js +36 -0
- package/dist/registry-5nuYFMd4.js.map +1 -0
- package/dist/renderer/types.d.ts +2 -2
- package/dist/share-CyrZiFqf.js +283 -0
- package/dist/share-CyrZiFqf.js.map +1 -0
- package/dist/source/types.d.ts +61 -2
- package/dist/types-DEz3mRLI.js +11 -0
- package/dist/types-DEz3mRLI.js.map +1 -0
- package/dist/{webglRenderer-BK-ArG1G.js → webglRenderer-BcNhTqG8.js} +33 -248
- package/dist/webglRenderer-BcNhTqG8.js.map +1 -0
- package/dist/zine.d.ts +176 -6
- package/options.schema.json +55 -2
- package/package.json +24 -2
- package/dist/types-CcPVyhOJ.js +0 -13
- package/dist/types-CcPVyhOJ.js.map +0 -1
- package/dist/webglRenderer-BK-ArG1G.js.map +0 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/controls/registry.ts
|
|
2
|
+
var e = /* @__PURE__ */ new Map();
|
|
3
|
+
function t(t) {
|
|
4
|
+
return e.set(t.id, t), t;
|
|
5
|
+
}
|
|
6
|
+
function n(t) {
|
|
7
|
+
return e.get(t);
|
|
8
|
+
}
|
|
9
|
+
function r(t) {
|
|
10
|
+
if (typeof t == "string") {
|
|
11
|
+
let n = e.get(t);
|
|
12
|
+
if (!n) throw Error(`controls: unknown control '${t}'. Register it with defineControl() or pass a definition object.`);
|
|
13
|
+
return n;
|
|
14
|
+
}
|
|
15
|
+
let n = e.get(t.id);
|
|
16
|
+
return n ? {
|
|
17
|
+
...n,
|
|
18
|
+
...t
|
|
19
|
+
} : t;
|
|
20
|
+
}
|
|
21
|
+
var i = [
|
|
22
|
+
"prev",
|
|
23
|
+
"pageInput",
|
|
24
|
+
"next",
|
|
25
|
+
"|",
|
|
26
|
+
"zoomOut",
|
|
27
|
+
"zoomIn",
|
|
28
|
+
"search",
|
|
29
|
+
"share",
|
|
30
|
+
"menu",
|
|
31
|
+
"fullscreen"
|
|
32
|
+
];
|
|
33
|
+
//#endregion
|
|
34
|
+
export { r as i, t as n, n as r, i as t };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=registry-5nuYFMd4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry-5nuYFMd4.js","names":[],"sources":["../src/controls/registry.ts"],"sourcesContent":["import type { ControlDef, ControlItem } from './types';\n\n/**\n * The control registry.\n *\n * Deliberately just the mechanism — no icons, no built-in definitions — because `defineControl`\n * has to be callable before the toolbar chunk loads, so this module lands in the base bundle.\n * The built-ins live in `./builtins`, which the lazy chunk pulls in along with the icon set.\n */\nconst registry = new Map<string, ControlDef>();\n\n/**\n * Add a control, or replace one by reusing its id. Returns the definition so it can be dropped\n * straight into a layout.\n *\n * ```js\n * defineControl({ id: 'print', title: 'Print', icon: '<path d=\"…\"/>', action: () => print() });\n * new Zine(el, { source, controls: { items: ['prev', 'next', 'print'] } });\n * ```\n */\nexport function defineControl(def: ControlDef): ControlDef {\n registry.set(def.id, def);\n return def;\n}\n\n/** Look up a registered control. */\nexport function getControl(id: string): ControlDef | undefined {\n return registry.get(id);\n}\n\n/**\n * Turn a layout entry into a concrete definition. A bare string names a registered control; an\n * object with only some fields is layered over the registered one, so `{ id: 'next', title: 'Go\n * on' }` relabels the built-in without reimplementing it.\n */\nexport function resolveControl(item: ControlItem): ControlDef {\n if (typeof item === 'string') {\n const found = registry.get(item);\n if (!found) {\n throw new Error(\n `controls: unknown control '${item}'. Register it with defineControl() or pass a definition object.`,\n );\n }\n return found;\n }\n const base = registry.get(item.id);\n if (!base) return item as ControlDef;\n return { ...base, ...item };\n}\n\n/** The toolbar shown when `items` is not given. */\nexport const DEFAULT_ITEMS: readonly ControlItem[] = [\n 'prev',\n 'pageInput',\n 'next',\n '|',\n 'zoomOut',\n 'zoomIn',\n 'search',\n 'share',\n 'menu',\n 'fullscreen',\n];\n"],"mappings":";AASA,IAAM,oBAAW,IAAI,IAAwB;AAW7C,SAAgB,EAAc,GAA6B;CAEzD,OADA,EAAS,IAAI,EAAI,IAAI,CAAG,GACjB;AACT;AAGA,SAAgB,EAAW,GAAoC;CAC7D,OAAO,EAAS,IAAI,CAAE;AACxB;AAOA,SAAgB,EAAe,GAA+B;CAC5D,IAAI,OAAO,KAAS,UAAU;EAC5B,IAAM,IAAQ,EAAS,IAAI,CAAI;EAC/B,IAAI,CAAC,GACH,MAAU,MACR,8BAA8B,EAAK,iEACrC;EAEF,OAAO;CACT;CACA,IAAM,IAAO,EAAS,IAAI,EAAK,EAAE;CAEjC,OADK,IACE;EAAE,GAAG;EAAM,GAAG;CAAK,IADR;AAEpB;AAGA,IAAa,IAAwC;CACnD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
|
package/dist/renderer/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Spread } from '../engine/spread';
|
|
2
|
-
import type {
|
|
2
|
+
import type { CurlSpec, CurlAnchor } from '../geometry/curls/types';
|
|
3
3
|
/** A decoded, ready-to-paint raster for one page (produced by a content source). */
|
|
4
4
|
export type PageContent = ImageBitmap | HTMLCanvasElement;
|
|
5
5
|
/** Resolved content for the two halves of a spread; null = blank side. */
|
|
@@ -12,7 +12,7 @@ export interface RenderOptions {
|
|
|
12
12
|
/** Render a lone page filling the container (single-page mode), not a half panel. */
|
|
13
13
|
fill?: boolean;
|
|
14
14
|
/** Which page-curl model the WebGL2 renderer bends the leaf with (ignored by CSS). */
|
|
15
|
-
curl?:
|
|
15
|
+
curl?: CurlSpec;
|
|
16
16
|
/** Corner the fold originates from for anchored curls; y in [0,1] (where the reader tapped). */
|
|
17
17
|
anchor?: CurlAnchor;
|
|
18
18
|
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { n as e, t } from "./icons-D87p7JMP.js";
|
|
2
|
+
//#region src/controls/socials.ts
|
|
3
|
+
var n = encodeURIComponent, r = [
|
|
4
|
+
{
|
|
5
|
+
id: "facebook",
|
|
6
|
+
label: "Facebook",
|
|
7
|
+
icon: "<path fill=\"currentColor\" stroke=\"none\" d=\"M22 12a10 10 0 1 0-11.56 9.88v-6.99H7.9V12h2.54V9.8c0-2.5 1.49-3.89 3.77-3.89 1.1 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56V12h2.78l-.45 2.89h-2.33v6.99A10 10 0 0 0 22 12Z\"/>",
|
|
8
|
+
href: (e) => `https://www.facebook.com/sharer/sharer.php?u=${n(e)}`
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "x",
|
|
12
|
+
label: "X (Twitter)",
|
|
13
|
+
icon: "<path fill=\"currentColor\" stroke=\"none\" d=\"M17.53 3h3.06l-6.69 7.64L21.75 21h-6.16l-4.82-6.3L5.24 21H2.18l7.15-8.17L2.25 3h6.32l4.36 5.77L17.53 3Zm-1.07 16.17h1.69L7.62 4.73H5.8l10.66 14.44Z\"/>",
|
|
14
|
+
href: (e, t) => `https://twitter.com/intent/tweet?url=${n(e)}&text=${n(t)}`
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "linkedin",
|
|
18
|
+
label: "LinkedIn",
|
|
19
|
+
icon: "<path fill=\"currentColor\" stroke=\"none\" d=\"M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05a3.74 3.74 0 0 1 3.37-1.85c3.6 0 4.27 2.37 4.27 5.46v6.28ZM5.34 7.43a2.07 2.07 0 1 1 0-4.13 2.07 2.07 0 0 1 0 4.13Zm1.78 13.02H3.55V9h3.57v11.45ZM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0Z\"/>",
|
|
20
|
+
href: (e) => `https://www.linkedin.com/sharing/share-offsite/?url=${n(e)}`
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "whatsapp",
|
|
24
|
+
label: "WhatsApp",
|
|
25
|
+
icon: "<path fill=\"currentColor\" stroke=\"none\" d=\"M12.04 2a9.9 9.9 0 0 0-8.5 14.9L2 22l5.25-1.38A9.9 9.9 0 1 0 12.04 2Zm5.8 14.06c-.25.7-1.44 1.33-1.99 1.38-.53.05-1.02.24-3.44-.72-2.9-1.14-4.73-4.1-4.87-4.29-.14-.19-1.16-1.54-1.16-2.94s.73-2.08 1-2.37c.26-.28.57-.35.76-.35h.55c.17 0 .42-.07.65.5.24.58.8 2 .87 2.14.07.14.12.31.02.5-.09.19-.14.3-.28.47l-.42.48c-.14.14-.28.29-.12.57.16.28.7 1.16 1.51 1.88 1.04.93 1.91 1.21 2.19 1.35.28.15.44.12.6-.07.17-.19.7-.81.88-1.09.19-.28.37-.23.63-.14.25.1 1.63.77 1.9.91.29.14.48.21.55.33.07.11.07.67-.18 1.37Z\"/>",
|
|
26
|
+
href: (e, t) => `https://api.whatsapp.com/send?text=${n(`${t} ${e}`)}`
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "pinterest",
|
|
30
|
+
label: "Pinterest",
|
|
31
|
+
icon: "<path fill=\"currentColor\" stroke=\"none\" d=\"M12 2a10 10 0 0 0-3.65 19.31c-.09-.78-.17-1.98.03-2.83.19-.78 1.2-4.98 1.2-4.98s-.3-.61-.3-1.52c0-1.42.82-2.48 1.85-2.48.87 0 1.3.66 1.3 1.44 0 .88-.56 2.2-.85 3.42-.24 1.02.51 1.86 1.52 1.86 1.83 0 3.23-1.93 3.23-4.7 0-2.46-1.77-4.18-4.29-4.18-2.92 0-4.64 2.19-4.64 4.46 0 .88.34 1.83.76 2.35a.3.3 0 0 1 .07.29l-.28 1.16c-.05.19-.15.23-.34.14-1.28-.6-2.08-2.47-2.08-3.98 0-3.24 2.35-6.21 6.79-6.21 3.56 0 6.33 2.54 6.33 5.93 0 3.54-2.23 6.39-5.32 6.39-1.04 0-2.02-.54-2.35-1.18l-.64 2.44c-.23.89-.86 2.01-1.28 2.69A10 10 0 1 0 12 2Z\"/>",
|
|
32
|
+
href: (e, t) => `https://pinterest.com/pin/create/button/?url=${n(e)}&description=${n(t)}`
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "email",
|
|
36
|
+
label: "Email",
|
|
37
|
+
icon: "<path fill=\"currentColor\" stroke=\"none\" d=\"M20 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2Zm0 4.24-7.47 4.67a1 1 0 0 1-1.06 0L4 8.24V6.4l8 5 8-5v1.84Z\"/>",
|
|
38
|
+
href: (e, t) => `mailto:?subject=${n(t)}&body=${n(e)}`
|
|
39
|
+
}
|
|
40
|
+
], i = [
|
|
41
|
+
16,
|
|
42
|
+
28,
|
|
43
|
+
44,
|
|
44
|
+
64,
|
|
45
|
+
86,
|
|
46
|
+
108,
|
|
47
|
+
124,
|
|
48
|
+
154,
|
|
49
|
+
182,
|
|
50
|
+
216
|
|
51
|
+
], a = [
|
|
52
|
+
10,
|
|
53
|
+
16,
|
|
54
|
+
26,
|
|
55
|
+
18,
|
|
56
|
+
24,
|
|
57
|
+
16,
|
|
58
|
+
18,
|
|
59
|
+
22,
|
|
60
|
+
22,
|
|
61
|
+
26
|
|
62
|
+
], o = [
|
|
63
|
+
1,
|
|
64
|
+
1,
|
|
65
|
+
1,
|
|
66
|
+
2,
|
|
67
|
+
2,
|
|
68
|
+
4,
|
|
69
|
+
4,
|
|
70
|
+
4,
|
|
71
|
+
5,
|
|
72
|
+
5
|
|
73
|
+
], s = [
|
|
74
|
+
[],
|
|
75
|
+
[6, 18],
|
|
76
|
+
[6, 22],
|
|
77
|
+
[6, 26],
|
|
78
|
+
[6, 30],
|
|
79
|
+
[6, 34],
|
|
80
|
+
[
|
|
81
|
+
6,
|
|
82
|
+
22,
|
|
83
|
+
38
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
6,
|
|
87
|
+
24,
|
|
88
|
+
42
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
6,
|
|
92
|
+
26,
|
|
93
|
+
46
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
6,
|
|
97
|
+
28,
|
|
98
|
+
50
|
|
99
|
+
]
|
|
100
|
+
], c = /* @__PURE__ */ new Uint8Array(512), l = /* @__PURE__ */ new Uint8Array(256);
|
|
101
|
+
for (let e = 0, t = 1; e < 255; e++) c[e] = t, l[t] = e, t <<= 1, t & 256 && (t ^= 285);
|
|
102
|
+
for (let e = 255; e < 512; e++) c[e] = c[e - 255];
|
|
103
|
+
var u = (e, t) => e === 0 || t === 0 ? 0 : c[(l[e] + l[t]) % 255];
|
|
104
|
+
function d(e) {
|
|
105
|
+
let t = new Uint8Array([1]);
|
|
106
|
+
for (let n = 0; n < e; n++) {
|
|
107
|
+
let e = new Uint8Array(t.length + 1);
|
|
108
|
+
for (let r = 0; r < t.length; r++) e[r] = e[r] ^ t[r], e[r + 1] = e[r + 1] ^ u(t[r], c[n]);
|
|
109
|
+
t = e;
|
|
110
|
+
}
|
|
111
|
+
return t;
|
|
112
|
+
}
|
|
113
|
+
function f(e, t) {
|
|
114
|
+
let n = d(t), r = new Uint8Array(t);
|
|
115
|
+
for (let i of e) {
|
|
116
|
+
let e = i ^ r[0];
|
|
117
|
+
if (r.copyWithin(0, 1), r[t - 1] = 0, e !== 0) for (let i = 0; i < t; i++) r[i] = r[i] ^ u(n[i + 1], e);
|
|
118
|
+
}
|
|
119
|
+
return r;
|
|
120
|
+
}
|
|
121
|
+
function p(e) {
|
|
122
|
+
let t = 0 | e, n = t;
|
|
123
|
+
for (let e = 0; e < 10; e++) n = n << 1 ^ (n >>> 9) * 1335;
|
|
124
|
+
return (t << 10 | n) ^ 21522;
|
|
125
|
+
}
|
|
126
|
+
var m = (e, t, n, r, i = !0) => {
|
|
127
|
+
e.modules[n * e.size + t] = +!!r, i && (e.reserved[n * e.size + t] = 1);
|
|
128
|
+
};
|
|
129
|
+
function h(e, t, n) {
|
|
130
|
+
for (let r = -1; r <= 7; r++) for (let i = -1; i <= 7; i++) {
|
|
131
|
+
let a = t + i, o = n + r;
|
|
132
|
+
if (a < 0 || o < 0 || a >= e.size || o >= e.size) continue;
|
|
133
|
+
let s = Math.max(Math.abs(i - 3), Math.abs(r - 3));
|
|
134
|
+
m(e, a, o, s !== 2 && s <= 3);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function g(e) {
|
|
138
|
+
let t = new TextEncoder().encode(e), n = i.findIndex((e) => t.length + 2 <= e) + 1;
|
|
139
|
+
if (n === 0) throw Error(`qr: ${t.length} bytes is too long for this encoder (max 214).`);
|
|
140
|
+
let r = i[n - 1], c = [], l = (e, t) => {
|
|
141
|
+
for (let n = t - 1; n >= 0; n--) c.push(e >>> n & 1);
|
|
142
|
+
};
|
|
143
|
+
l(4, 4), l(t.length, n < 10 ? 8 : 16);
|
|
144
|
+
for (let e of t) l(e, 8);
|
|
145
|
+
for (l(0, Math.min(4, r * 8 - c.length)); c.length % 8;) c.push(0);
|
|
146
|
+
let u = new Uint8Array(r);
|
|
147
|
+
for (let e = 0; e < c.length; e += 8) {
|
|
148
|
+
let t = 0;
|
|
149
|
+
for (let n = 0; n < 8; n++) t = t << 1 | c[e + n];
|
|
150
|
+
u[e / 8] = t;
|
|
151
|
+
}
|
|
152
|
+
for (let e = c.length / 8, t = 0; e < r; e++, t++) u[e] = t % 2 == 0 ? 236 : 17;
|
|
153
|
+
let d = o[n - 1], g = a[n - 1], _ = Math.floor(r / d), v = r % d, y = [], b = [];
|
|
154
|
+
for (let e = 0, t = 0; e < d; e++) {
|
|
155
|
+
let n = _ + +(e >= d - v), r = u.subarray(t, t + n);
|
|
156
|
+
t += n, y.push(r), b.push(f(r, g));
|
|
157
|
+
}
|
|
158
|
+
let x = [];
|
|
159
|
+
for (let e = 0; e < _ + 1; e++) for (let t of y) e < t.length && x.push(t[e]);
|
|
160
|
+
for (let e = 0; e < g; e++) for (let t of b) x.push(t[e]);
|
|
161
|
+
let S = n * 4 + 17, C = {
|
|
162
|
+
size: S,
|
|
163
|
+
modules: new Uint8Array(S * S),
|
|
164
|
+
reserved: new Uint8Array(S * S)
|
|
165
|
+
};
|
|
166
|
+
h(C, 0, 0), h(C, S - 7, 0), h(C, 0, S - 7);
|
|
167
|
+
for (let e = 8; e < S - 8; e++) {
|
|
168
|
+
let t = e % 2 == 0;
|
|
169
|
+
m(C, e, 6, t), m(C, 6, e, t);
|
|
170
|
+
}
|
|
171
|
+
for (let e of s[n - 1]) for (let t of s[n - 1]) if (!(e < 9 && t < 9 || e < 9 && t > S - 10 || e > S - 10 && t < 9)) for (let n = -2; n <= 2; n++) for (let r = -2; r <= 2; r++) m(C, e + r, t + n, Math.max(Math.abs(r), Math.abs(n)) !== 1);
|
|
172
|
+
m(C, 8, S - 8, !0);
|
|
173
|
+
for (let e = 0; e < 9; e++) e !== 6 && (m(C, e, 8, !1), m(C, 8, e, !1));
|
|
174
|
+
for (let e = 0; e < 8; e++) m(C, S - 1 - e, 8, !1), m(C, 8, S - 1 - e, !1);
|
|
175
|
+
m(C, 8, S - 8, !0);
|
|
176
|
+
let w = 0, T = x.length * 8;
|
|
177
|
+
for (let e = S - 1; e >= 1; e -= 2) {
|
|
178
|
+
e === 6 && (e = 5);
|
|
179
|
+
for (let t = 0; t < S; t++) for (let n = 0; n < 2; n++) {
|
|
180
|
+
let r = e - n, i = e + 1 & 2 ? t : S - 1 - t;
|
|
181
|
+
if (C.reserved[i * S + r]) continue;
|
|
182
|
+
let a = !1;
|
|
183
|
+
w < T && (a = (x[w >>> 3] >>> 7 - (w & 7) & 1) == 1, w++), (r + i) % 2 == 0 && (a = !a), m(C, r, i, a, !1);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
let E = p(0);
|
|
187
|
+
for (let e = 0; e < 15; e++) {
|
|
188
|
+
let t = (E >>> e & 1) == 1, n = e < 6 ? e : e < 8 ? e + 1 : 8, r = e < 8 ? 8 : e < 9 ? 7 : 14 - e;
|
|
189
|
+
m(C, n, 8, t), m(C, 8, r, t), e < 8 ? m(C, S - 1 - e, 8, t) : m(C, 8, S - 15 + e, t);
|
|
190
|
+
}
|
|
191
|
+
return m(C, 8, S - 8, !0), {
|
|
192
|
+
size: S,
|
|
193
|
+
modules: C.modules
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function _(e, t) {
|
|
197
|
+
let { size: n, modules: r } = g(e), i = "";
|
|
198
|
+
for (let e = 0; e < n; e++) for (let t = 0; t < n; t++) r[e * n + t] && (i += `M${t} ${e}h1v1h-1z`);
|
|
199
|
+
let a = n + 4;
|
|
200
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}" viewBox="0 0 ${a} ${a}" shape-rendering="crispEdges"><rect width="${a}" height="${a}" fill="#fff"/><g transform="translate(2 2)" fill="#000">${`<path d="${i}"/>`}</g></svg>`;
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/controls/share.ts
|
|
204
|
+
var v = 132, y = 1600, b = class {
|
|
205
|
+
#e;
|
|
206
|
+
#t;
|
|
207
|
+
#n;
|
|
208
|
+
#r;
|
|
209
|
+
#i = null;
|
|
210
|
+
#a;
|
|
211
|
+
constructor(n, r) {
|
|
212
|
+
let i = r.ownerDocument;
|
|
213
|
+
this.#e = i, this.#r = i.activeElement;
|
|
214
|
+
let a = n.pageLink(), o = i.title || "Flipbook";
|
|
215
|
+
this.#t = i.createElement("div"), this.#t.className = "zine-share-backdrop", this.#t.addEventListener("pointerdown", (e) => {
|
|
216
|
+
e.target === this.#t && this.close();
|
|
217
|
+
}), this.#n = i.createElement("div"), this.#n.className = "zine-share", this.#n.setAttribute("role", "dialog"), this.#n.setAttribute("aria-modal", "true"), this.#n.setAttribute("aria-label", "Share"), this.#t.appendChild(this.#n);
|
|
218
|
+
let s = i.createElement("div");
|
|
219
|
+
s.className = "zine-share-head";
|
|
220
|
+
let c = i.createElement("strong");
|
|
221
|
+
c.textContent = "Share";
|
|
222
|
+
let l = i.createElement("button");
|
|
223
|
+
l.type = "button", l.className = "zine-share-close", l.setAttribute("aria-label", "Close"), l.appendChild(e(i, t.close)), l.addEventListener("click", () => this.close()), s.append(c, l), this.#n.append(s, this.#s(i, a), this.#l(i, a, o));
|
|
224
|
+
let u = this.#o(i, a);
|
|
225
|
+
u && this.#n.insertBefore(u, this.#n.children[1]), r.ownerDocument.body.appendChild(this.#t), this.#a = (e) => {
|
|
226
|
+
e.key === "Escape" && this.close(), e.key === "Tab" && this.#u(e);
|
|
227
|
+
}, this.#t.addEventListener("keydown", this.#a), l.focus();
|
|
228
|
+
}
|
|
229
|
+
#o(e, t) {
|
|
230
|
+
if (!t) return null;
|
|
231
|
+
try {
|
|
232
|
+
let n = e.createElement("div");
|
|
233
|
+
return n.className = "zine-share-qr", n.innerHTML = _(t, v), n.setAttribute("aria-label", "QR code for this page"), n.setAttribute("role", "img"), n;
|
|
234
|
+
} catch {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
#s(e, t) {
|
|
239
|
+
let n = e.createElement("div");
|
|
240
|
+
n.className = "zine-share-link";
|
|
241
|
+
let r = e.createElement("input");
|
|
242
|
+
r.type = "text", r.readOnly = !0, r.value = t, r.setAttribute("aria-label", "Link to this page"), r.addEventListener("focus", () => r.select());
|
|
243
|
+
let i = e.createElement("button");
|
|
244
|
+
return i.type = "button", i.className = "zine-share-copy", i.textContent = "Copy", i.addEventListener("click", () => {
|
|
245
|
+
this.#c(t, i);
|
|
246
|
+
}), n.append(r, i), n;
|
|
247
|
+
}
|
|
248
|
+
async #c(e, t) {
|
|
249
|
+
try {
|
|
250
|
+
await navigator.clipboard?.writeText(e), t.textContent = "Copied";
|
|
251
|
+
} catch {
|
|
252
|
+
t.textContent = "Press Ctrl+C";
|
|
253
|
+
}
|
|
254
|
+
this.#i && clearTimeout(this.#i), this.#i = setTimeout(() => {
|
|
255
|
+
t.textContent = "Copy";
|
|
256
|
+
}, y);
|
|
257
|
+
}
|
|
258
|
+
#l(t, n, i) {
|
|
259
|
+
let a = t.createElement("div");
|
|
260
|
+
a.className = "zine-share-socials";
|
|
261
|
+
for (let o of r) {
|
|
262
|
+
let r = t.createElement("a");
|
|
263
|
+
r.className = "zine-share-social", r.href = o.href(n, i), r.title = o.label, r.setAttribute("aria-label", `Share on ${o.label}`), o.id !== "email" && (r.target = "_blank", r.rel = "noopener noreferrer"), r.appendChild(e(t, o.icon)), a.appendChild(r);
|
|
264
|
+
}
|
|
265
|
+
return a;
|
|
266
|
+
}
|
|
267
|
+
#u(e) {
|
|
268
|
+
let t = [...this.#n.querySelectorAll("button, a[href], input")].filter((e) => !e.hasAttribute("disabled"));
|
|
269
|
+
if (t.length === 0) return;
|
|
270
|
+
let n = t[0], r = t.at(-1), i = this.#e.activeElement;
|
|
271
|
+
e.shiftKey && i === n ? (r.focus(), e.preventDefault()) : !e.shiftKey && i === r && (n.focus(), e.preventDefault());
|
|
272
|
+
}
|
|
273
|
+
close() {
|
|
274
|
+
this.destroy();
|
|
275
|
+
}
|
|
276
|
+
destroy() {
|
|
277
|
+
this.#i && clearTimeout(this.#i), this.#i = null, this.#t.removeEventListener("keydown", this.#a), this.#t.remove(), this.#r?.focus?.();
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
//#endregion
|
|
281
|
+
export { b as ShareDialog };
|
|
282
|
+
|
|
283
|
+
//# sourceMappingURL=share-CyrZiFqf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share-CyrZiFqf.js","names":["#doc","#lastFocus","#backdrop","#dialog","#linkRow","#socialRow","#qr","#onKey","#trapFocus","#copy","#timer"],"sources":["../src/controls/socials.ts","../src/controls/qr.ts","../src/controls/share.ts"],"sourcesContent":["/**\n * Where a reader can send a link, and how each service wants it.\n *\n * These are plain share-intent URLs — no SDKs, no third-party scripts, nothing that could track\n * the reader before they choose to share. Each brand mark is drawn from its own official glyph;\n * they are filled shapes rather than the stroked outlines used elsewhere in the toolbar, so they\n * carry `fill=\"currentColor\"` and no stroke.\n */\nexport interface Social {\n id: string;\n label: string;\n /** Inner SVG markup for a 24x24 viewBox, filled rather than stroked. */\n icon: string;\n href(url: string, title: string): string;\n}\n\nconst e = encodeURIComponent;\n\nexport const SOCIALS: readonly Social[] = [\n {\n id: 'facebook',\n label: 'Facebook',\n icon: '<path fill=\"currentColor\" stroke=\"none\" d=\"M22 12a10 10 0 1 0-11.56 9.88v-6.99H7.9V12h2.54V9.8c0-2.5 1.49-3.89 3.77-3.89 1.1 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56V12h2.78l-.45 2.89h-2.33v6.99A10 10 0 0 0 22 12Z\"/>',\n href: (url) => `https://www.facebook.com/sharer/sharer.php?u=${e(url)}`,\n },\n {\n id: 'x',\n label: 'X (Twitter)',\n icon: '<path fill=\"currentColor\" stroke=\"none\" d=\"M17.53 3h3.06l-6.69 7.64L21.75 21h-6.16l-4.82-6.3L5.24 21H2.18l7.15-8.17L2.25 3h6.32l4.36 5.77L17.53 3Zm-1.07 16.17h1.69L7.62 4.73H5.8l10.66 14.44Z\"/>',\n href: (url, title) => `https://twitter.com/intent/tweet?url=${e(url)}&text=${e(title)}`,\n },\n {\n id: 'linkedin',\n label: 'LinkedIn',\n icon: '<path fill=\"currentColor\" stroke=\"none\" d=\"M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05a3.74 3.74 0 0 1 3.37-1.85c3.6 0 4.27 2.37 4.27 5.46v6.28ZM5.34 7.43a2.07 2.07 0 1 1 0-4.13 2.07 2.07 0 0 1 0 4.13Zm1.78 13.02H3.55V9h3.57v11.45ZM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0Z\"/>',\n href: (url) => `https://www.linkedin.com/sharing/share-offsite/?url=${e(url)}`,\n },\n {\n id: 'whatsapp',\n label: 'WhatsApp',\n icon: '<path fill=\"currentColor\" stroke=\"none\" d=\"M12.04 2a9.9 9.9 0 0 0-8.5 14.9L2 22l5.25-1.38A9.9 9.9 0 1 0 12.04 2Zm5.8 14.06c-.25.7-1.44 1.33-1.99 1.38-.53.05-1.02.24-3.44-.72-2.9-1.14-4.73-4.1-4.87-4.29-.14-.19-1.16-1.54-1.16-2.94s.73-2.08 1-2.37c.26-.28.57-.35.76-.35h.55c.17 0 .42-.07.65.5.24.58.8 2 .87 2.14.07.14.12.31.02.5-.09.19-.14.3-.28.47l-.42.48c-.14.14-.28.29-.12.57.16.28.7 1.16 1.51 1.88 1.04.93 1.91 1.21 2.19 1.35.28.15.44.12.6-.07.17-.19.7-.81.88-1.09.19-.28.37-.23.63-.14.25.1 1.63.77 1.9.91.29.14.48.21.55.33.07.11.07.67-.18 1.37Z\"/>',\n href: (url, title) => `https://api.whatsapp.com/send?text=${e(`${title} ${url}`)}`,\n },\n {\n id: 'pinterest',\n label: 'Pinterest',\n icon: '<path fill=\"currentColor\" stroke=\"none\" d=\"M12 2a10 10 0 0 0-3.65 19.31c-.09-.78-.17-1.98.03-2.83.19-.78 1.2-4.98 1.2-4.98s-.3-.61-.3-1.52c0-1.42.82-2.48 1.85-2.48.87 0 1.3.66 1.3 1.44 0 .88-.56 2.2-.85 3.42-.24 1.02.51 1.86 1.52 1.86 1.83 0 3.23-1.93 3.23-4.7 0-2.46-1.77-4.18-4.29-4.18-2.92 0-4.64 2.19-4.64 4.46 0 .88.34 1.83.76 2.35a.3.3 0 0 1 .07.29l-.28 1.16c-.05.19-.15.23-.34.14-1.28-.6-2.08-2.47-2.08-3.98 0-3.24 2.35-6.21 6.79-6.21 3.56 0 6.33 2.54 6.33 5.93 0 3.54-2.23 6.39-5.32 6.39-1.04 0-2.02-.54-2.35-1.18l-.64 2.44c-.23.89-.86 2.01-1.28 2.69A10 10 0 1 0 12 2Z\"/>',\n href: (url, title) => `https://pinterest.com/pin/create/button/?url=${e(url)}&description=${e(title)}`,\n },\n {\n id: 'email',\n label: 'Email',\n icon: '<path fill=\"currentColor\" stroke=\"none\" d=\"M20 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2Zm0 4.24-7.47 4.67a1 1 0 0 1-1.06 0L4 8.24V6.4l8 5 8-5v1.84Z\"/>',\n href: (url, title) => `mailto:?subject=${e(title)}&body=${e(url)}`,\n },\n];\n","/**\n * A small QR encoder, enough to turn a URL into a scannable grid.\n *\n * Written rather than pulled in so the package keeps its \"no dependencies\" promise, and so no\n * reader's document URL is handed to a third-party image service to be logged. Deliberately\n * narrow: byte mode, error-correction level M, versions 1-10 (up to 271 characters at level M),\n * which covers any sane page URL. Longer input throws rather than silently producing a code that\n * will not scan.\n */\n\n/** Data codewords available per version at error-correction level M, versions 1-10. */\nconst DATA_CODEWORDS = [16, 28, 44, 64, 86, 108, 124, 154, 182, 216];\n/** Error-correction codewords per block, level M, versions 1-10. */\nconst EC_PER_BLOCK = [10, 16, 26, 18, 24, 16, 18, 22, 22, 26];\n/** How many EC blocks the data is split into, level M, versions 1-10. */\nconst BLOCKS = [1, 1, 1, 2, 2, 4, 4, 4, 5, 5];\n/** Where alignment patterns sit, per version (empty for version 1). */\nconst ALIGNMENT = [\n [],\n [6, 18],\n [6, 22],\n [6, 26],\n [6, 30],\n [6, 34],\n [6, 22, 38],\n [6, 24, 42],\n [6, 26, 46],\n [6, 28, 50],\n];\n\n// Galois field tables for GF(256), used by the Reed-Solomon error correction.\nconst EXP = new Uint8Array(512);\nconst LOG = new Uint8Array(256);\nfor (let i = 0, x = 1; i < 255; i++) {\n EXP[i] = x;\n LOG[x] = i;\n x <<= 1;\n if (x & 0x100) x ^= 0x11d; // the QR field's primitive polynomial\n}\nfor (let i = 255; i < 512; i++) EXP[i] = EXP[i - 255]!;\n\nconst mul = (a: number, b: number): number =>\n a === 0 || b === 0 ? 0 : EXP[(LOG[a]! + LOG[b]!) % 255]!;\n\n/** The generator polynomial for `degree` error-correction codewords. */\nfunction generator(degree: number): Uint8Array {\n let poly = new Uint8Array([1]);\n for (let i = 0; i < degree; i++) {\n const next = new Uint8Array(poly.length + 1);\n for (let j = 0; j < poly.length; j++) {\n // Written as explicit reads: a compound assignment on an indexed element is what\n // noUncheckedIndexedAccess objects to, since the target is possibly-undefined.\n next[j] = next[j]! ^ poly[j]!;\n next[j + 1] = next[j + 1]! ^ mul(poly[j]!, EXP[i]!);\n }\n poly = next;\n }\n return poly;\n}\n\n/** Reed-Solomon remainder: the error-correction codewords for one block. */\nfunction ecBlock(data: Uint8Array, count: number): Uint8Array {\n const gen = generator(count);\n const out = new Uint8Array(count);\n for (const byte of data) {\n const factor = byte ^ out[0]!;\n out.copyWithin(0, 1);\n out[count - 1] = 0;\n if (factor !== 0) {\n for (let i = 0; i < count; i++) out[i] = out[i]! ^ mul(gen[i + 1]!, factor);\n }\n }\n return out;\n}\n\n/** The 15-bit format string for level M and a mask, with its BCH check bits. */\nfunction formatBits(mask: number): number {\n const data = (0b00 << 3) | mask; // 00 = level M\n let rem = data;\n for (let i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537);\n return ((data << 10) | rem) ^ 0x5412;\n}\n\ntype Grid = { size: number; modules: Uint8Array; reserved: Uint8Array };\n\nconst at = (g: Grid, x: number, y: number): number => g.modules[y * g.size + x]!;\nconst set = (g: Grid, x: number, y: number, dark: boolean, reserve = true): void => {\n g.modules[y * g.size + x] = dark ? 1 : 0;\n if (reserve) g.reserved[y * g.size + x] = 1;\n};\n\nfunction placeFinder(g: Grid, cx: number, cy: number): void {\n for (let dy = -1; dy <= 7; dy++) {\n for (let dx = -1; dx <= 7; dx++) {\n const x = cx + dx;\n const y = cy + dy;\n if (x < 0 || y < 0 || x >= g.size || y >= g.size) continue;\n const d = Math.max(Math.abs(dx - 3), Math.abs(dy - 3));\n set(g, x, y, d !== 2 && d <= 3);\n }\n }\n}\n\n/** Encode `text` as a QR matrix: a square of 0/1 by row. */\nexport function encodeQr(text: string): { size: number; modules: Uint8Array } {\n const bytes = new TextEncoder().encode(text);\n const version = DATA_CODEWORDS.findIndex((cap) => bytes.length + 2 <= cap) + 1;\n if (version === 0) {\n throw new Error(`qr: ${bytes.length} bytes is too long for this encoder (max 214).`);\n }\n\n // --- bit stream: mode, length, payload, terminator, padding ---\n const capacity = DATA_CODEWORDS[version - 1]!;\n const bits: number[] = [];\n const push = (value: number, width: number): void => {\n for (let i = width - 1; i >= 0; i--) bits.push((value >>> i) & 1);\n };\n push(0b0100, 4); // byte mode\n push(bytes.length, version < 10 ? 8 : 16);\n for (const byte of bytes) push(byte, 8);\n push(0, Math.min(4, capacity * 8 - bits.length)); // terminator\n while (bits.length % 8) bits.push(0);\n const data = new Uint8Array(capacity);\n for (let i = 0; i < bits.length; i += 8) {\n let byte = 0;\n for (let b = 0; b < 8; b++) byte = (byte << 1) | bits[i + b]!;\n data[i / 8] = byte;\n }\n for (let i = bits.length / 8, pad = 0; i < capacity; i++, pad++) {\n data[i] = pad % 2 === 0 ? 0xec : 0x11; // the spec's alternating pad bytes\n }\n\n // --- split into blocks, compute EC, interleave ---\n const blockCount = BLOCKS[version - 1]!;\n const ecCount = EC_PER_BLOCK[version - 1]!;\n const shortLen = Math.floor(capacity / blockCount);\n const longCount = capacity % blockCount;\n const dataBlocks: Uint8Array[] = [];\n const ecBlocks: Uint8Array[] = [];\n for (let i = 0, offset = 0; i < blockCount; i++) {\n const len = shortLen + (i >= blockCount - longCount ? 1 : 0);\n const block = data.subarray(offset, offset + len);\n offset += len;\n dataBlocks.push(block);\n ecBlocks.push(ecBlock(block, ecCount));\n }\n const codewords: number[] = [];\n for (let i = 0; i < shortLen + 1; i++) {\n for (const block of dataBlocks) if (i < block.length) codewords.push(block[i]!);\n }\n for (let i = 0; i < ecCount; i++) {\n for (const block of ecBlocks) codewords.push(block[i]!);\n }\n\n // --- lay out the matrix ---\n const size = version * 4 + 17;\n const g: Grid = {\n size,\n modules: new Uint8Array(size * size),\n reserved: new Uint8Array(size * size),\n };\n placeFinder(g, 0, 0);\n placeFinder(g, size - 7, 0);\n placeFinder(g, 0, size - 7);\n for (let i = 8; i < size - 8; i++) {\n const dark = i % 2 === 0; // timing patterns\n set(g, i, 6, dark);\n set(g, 6, i, dark);\n }\n for (const ax of ALIGNMENT[version - 1]!) {\n for (const ay of ALIGNMENT[version - 1]!) {\n // Alignment patterns skip the corners already held by finders.\n if ((ax < 9 && ay < 9) || (ax < 9 && ay > size - 10) || (ax > size - 10 && ay < 9)) continue;\n for (let dy = -2; dy <= 2; dy++) {\n for (let dx = -2; dx <= 2; dx++) {\n set(g, ax + dx, ay + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1);\n }\n }\n }\n }\n set(g, 8, size - 8, true); // the always-dark module\n // Reserve the format areas so data does not land in them.\n for (let i = 0; i < 9; i++) {\n if (i !== 6) set(g, i, 8, false), set(g, 8, i, false);\n }\n for (let i = 0; i < 8; i++) set(g, size - 1 - i, 8, false), set(g, 8, size - 1 - i, false);\n set(g, 8, size - 8, true);\n\n // --- weave the codewords in, masking as we go (mask 0: (x + y) % 2) ---\n let bit = 0;\n const total = codewords.length * 8;\n for (let right = size - 1; right >= 1; right -= 2) {\n if (right === 6) right = 5; // the vertical timing column is skipped\n for (let step = 0; step < size; step++) {\n for (let col = 0; col < 2; col++) {\n const x = right - col;\n const upward = ((right + 1) & 2) === 0;\n const y = upward ? size - 1 - step : step;\n if (g.reserved[y * size + x]) continue;\n let dark = false;\n if (bit < total) {\n dark = ((codewords[bit >>> 3]! >>> (7 - (bit & 7))) & 1) === 1;\n bit++;\n }\n if ((x + y) % 2 === 0) dark = !dark; // mask 0\n set(g, x, y, dark, false);\n }\n }\n }\n\n // --- format information, now that the mask is known ---\n const format = formatBits(0);\n for (let i = 0; i < 15; i++) {\n const dark = ((format >>> i) & 1) === 1;\n // Around the top-left finder...\n const a = i < 6 ? i : i < 8 ? i + 1 : 8;\n const b = i < 8 ? 8 : i < 9 ? 7 : 14 - i;\n set(g, a, 8, dark);\n set(g, 8, b, dark);\n // ...and the copy split between the other two.\n if (i < 8) set(g, size - 1 - i, 8, dark);\n else set(g, 8, size - 15 + i, dark);\n }\n set(g, 8, size - 8, true);\n\n return { size, modules: g.modules };\n}\n\n/** Render a QR matrix as an SVG string, sized to `px`. */\nexport function qrSvg(text: string, px: number): string {\n const { size, modules } = encodeQr(text);\n let path = '';\n for (let y = 0; y < size; y++) {\n for (let x = 0; x < size; x++) {\n if (modules[y * size + x]) path += `M${x} ${y}h1v1h-1z`;\n }\n }\n const quiet = 2; // the spec asks for 4; 2 scans fine and wastes less space on screen\n const box = size + quiet * 2;\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${px}\" height=\"${px}\" viewBox=\"0 0 ${box} ${box}\" shape-rendering=\"crispEdges\"><rect width=\"${box}\" height=\"${box}\" fill=\"#fff\"/><g transform=\"translate(${quiet} ${quiet})\" fill=\"#000\">${`<path d=\"${path}\"/>`}</g></svg>`;\n}\n","import { createIcon, ICONS } from './icons';\nimport { SOCIALS } from './socials';\nimport { qrSvg } from './qr';\nimport type { Zine } from '../zine';\n\n/** QR edge length in CSS px. */\nconst QR_SIZE = 132;\n/** How long the copy button confirms for. */\nconst COPIED_MS = 1600;\n\n/**\n * The share dialog: a QR code, the link, a copy button and the usual social destinations.\n *\n * Loaded on demand — the whole module, its icons and the QR encoder arrive only when a reader\n * presses Share, so a book nobody shares pays nothing for it.\n */\nexport class ShareDialog {\n #doc: Document;\n #backdrop: HTMLElement;\n #dialog: HTMLElement;\n #lastFocus: Element | null;\n #timer: ReturnType<typeof setTimeout> | null = null;\n #onKey: (e: KeyboardEvent) => void;\n\n constructor(zine: Zine, container: HTMLElement) {\n const doc = container.ownerDocument!;\n this.#doc = doc;\n this.#lastFocus = doc.activeElement;\n\n const url = zine.pageLink();\n const title = doc.title || 'Flipbook';\n\n this.#backdrop = doc.createElement('div');\n this.#backdrop.className = 'zine-share-backdrop';\n this.#backdrop.addEventListener('pointerdown', (e) => {\n if (e.target === this.#backdrop) this.close();\n });\n\n this.#dialog = doc.createElement('div');\n this.#dialog.className = 'zine-share';\n this.#dialog.setAttribute('role', 'dialog');\n this.#dialog.setAttribute('aria-modal', 'true');\n this.#dialog.setAttribute('aria-label', 'Share');\n this.#backdrop.appendChild(this.#dialog);\n\n const head = doc.createElement('div');\n head.className = 'zine-share-head';\n const heading = doc.createElement('strong');\n heading.textContent = 'Share';\n const close = doc.createElement('button');\n close.type = 'button';\n close.className = 'zine-share-close';\n close.setAttribute('aria-label', 'Close');\n close.appendChild(createIcon(doc, ICONS.close));\n close.addEventListener('click', () => this.close());\n head.append(heading, close);\n\n this.#dialog.append(head, this.#linkRow(doc, url), this.#socialRow(doc, url, title));\n const qr = this.#qr(doc, url);\n if (qr) this.#dialog.insertBefore(qr, this.#dialog.children[1]!);\n\n container.ownerDocument.body.appendChild(this.#backdrop);\n\n this.#onKey = (e): void => {\n if (e.key === 'Escape') this.close();\n if (e.key === 'Tab') this.#trapFocus(e);\n };\n this.#backdrop.addEventListener('keydown', this.#onKey);\n close.focus();\n }\n\n /** The QR, or nothing if the URL is too long to encode — better no code than a broken one. */\n #qr(doc: Document, url: string): HTMLElement | null {\n if (!url) return null;\n try {\n const wrap = doc.createElement('div');\n wrap.className = 'zine-share-qr';\n wrap.innerHTML = qrSvg(url, QR_SIZE);\n wrap.setAttribute('aria-label', 'QR code for this page');\n wrap.setAttribute('role', 'img');\n return wrap;\n } catch {\n return null;\n }\n }\n\n #linkRow(doc: Document, url: string): HTMLElement {\n const row = doc.createElement('div');\n row.className = 'zine-share-link';\n const input = doc.createElement('input');\n input.type = 'text';\n input.readOnly = true;\n input.value = url;\n input.setAttribute('aria-label', 'Link to this page');\n input.addEventListener('focus', () => input.select());\n\n const copy = doc.createElement('button');\n copy.type = 'button';\n copy.className = 'zine-share-copy';\n copy.textContent = 'Copy';\n copy.addEventListener('click', () => {\n void this.#copy(url, copy);\n });\n row.append(input, copy);\n return row;\n }\n\n async #copy(url: string, button: HTMLButtonElement): Promise<void> {\n try {\n await navigator.clipboard?.writeText(url);\n button.textContent = 'Copied';\n } catch {\n button.textContent = 'Press Ctrl+C';\n }\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = setTimeout(() => {\n button.textContent = 'Copy';\n }, COPIED_MS);\n }\n\n #socialRow(doc: Document, url: string, title: string): HTMLElement {\n const row = doc.createElement('div');\n row.className = 'zine-share-socials';\n for (const social of SOCIALS) {\n const link = doc.createElement('a');\n link.className = 'zine-share-social';\n link.href = social.href(url, title);\n link.title = social.label;\n link.setAttribute('aria-label', `Share on ${social.label}`);\n if (social.id !== 'email') {\n link.target = '_blank';\n link.rel = 'noopener noreferrer';\n }\n link.appendChild(createIcon(doc, social.icon));\n row.appendChild(link);\n }\n return row;\n }\n\n /** Keep Tab inside the dialog while it is open. */\n #trapFocus(event: KeyboardEvent): void {\n const focusable = [\n ...this.#dialog.querySelectorAll<HTMLElement>('button, a[href], input'),\n ].filter((el) => !el.hasAttribute('disabled'));\n if (focusable.length === 0) return;\n const first = focusable[0]!;\n const last = focusable.at(-1)!;\n const active = this.#doc.activeElement;\n if (event.shiftKey && active === first) {\n last.focus();\n event.preventDefault();\n } else if (!event.shiftKey && active === last) {\n first.focus();\n event.preventDefault();\n }\n }\n\n close(): void {\n this.destroy();\n }\n\n destroy(): void {\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = null;\n this.#backdrop.removeEventListener('keydown', this.#onKey);\n this.#backdrop.remove();\n // Hand focus back to whatever opened the dialog.\n (this.#lastFocus as HTMLElement | null)?.focus?.();\n }\n}\n"],"mappings":"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"}
|
package/dist/source/types.d.ts
CHANGED
|
@@ -12,8 +12,15 @@ export interface Source {
|
|
|
12
12
|
* synchronously-known page count (e.g. ImageSource) can omit it.
|
|
13
13
|
*/
|
|
14
14
|
open?(): Promise<void>;
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Decode/resolve a single page to a raster.
|
|
17
|
+
*
|
|
18
|
+
* `opts` is a hint, not a contract: it is passed only while the reader is zoomed in, and a
|
|
19
|
+
* source is free to ignore it and return its normal full-page raster. Sources backed by a
|
|
20
|
+
* fixed-resolution original (images) have nothing sharper to offer; vector sources (PDF) can
|
|
21
|
+
* re-rasterize the requested region to recover the detail that magnifying pixels loses.
|
|
22
|
+
*/
|
|
23
|
+
get(index: number, opts?: PageRequest): Promise<PageContent>;
|
|
17
24
|
/** Hint that these pages will be needed soon; out-of-range indices are ignored. */
|
|
18
25
|
prefetch(indices: number[]): void;
|
|
19
26
|
/** Release any decoded resources. */
|
|
@@ -24,4 +31,56 @@ export interface Source {
|
|
|
24
31
|
* re-render that page if it's on screen.
|
|
25
32
|
*/
|
|
26
33
|
onPageUpdate?(handler: (index: number) => void): void;
|
|
34
|
+
/**
|
|
35
|
+
* Optional: the page's plain text, for search. Sources backed by images can't have any and
|
|
36
|
+
* simply omit this — `Zine.canSearch()` reports whether a book is searchable at all.
|
|
37
|
+
*/
|
|
38
|
+
getText?(index: number): Promise<string>;
|
|
39
|
+
/**
|
|
40
|
+
* Optional: the original document, for a download control. Return null when there is nothing
|
|
41
|
+
* to hand over — a source built from a pre-opened document has no bytes of its own, and an
|
|
42
|
+
* image book is not a single file at all.
|
|
43
|
+
*/
|
|
44
|
+
getDownload?(): Promise<DownloadInfo | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Optional: the document's table of contents, for an outline panel. Return an empty array when
|
|
47
|
+
* the document has none — PDFs frequently do not.
|
|
48
|
+
*/
|
|
49
|
+
getOutline?(): Promise<OutlineItem[]>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A request for part of a page at a particular magnification, sent when the reader zooms in.
|
|
53
|
+
*
|
|
54
|
+
* A source that honours it returns a raster of `region` alone, rendered at `scale` times the
|
|
55
|
+
* resolution it would normally use. Returning the whole page instead is always valid: the engine
|
|
56
|
+
* tells the two apart by comparing the raster's aspect against the region's.
|
|
57
|
+
*/
|
|
58
|
+
export interface PageRequest {
|
|
59
|
+
/** How much sharper than the fit-to-screen raster to render, matching the reader's zoom. */
|
|
60
|
+
scale: number;
|
|
61
|
+
/** The visible part of the page, as fractions of its width and height in [0,1]. */
|
|
62
|
+
region: {
|
|
63
|
+
x: number;
|
|
64
|
+
y: number;
|
|
65
|
+
width: number;
|
|
66
|
+
height: number;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** One entry in a document's table of contents. */
|
|
70
|
+
export interface OutlineItem {
|
|
71
|
+
/** The heading text. */
|
|
72
|
+
title: string;
|
|
73
|
+
/** Zero-based page it points at, or null when the destination cannot be resolved. */
|
|
74
|
+
page: number | null;
|
|
75
|
+
/** Nested entries; empty for a leaf. */
|
|
76
|
+
children: OutlineItem[];
|
|
77
|
+
}
|
|
78
|
+
/** A downloadable original document. */
|
|
79
|
+
export interface DownloadInfo {
|
|
80
|
+
/** Where to fetch it, or a blob/object URL the caller can link to directly. */
|
|
81
|
+
url: string;
|
|
82
|
+
/** Suggested file name, used for the download attribute. */
|
|
83
|
+
filename: string;
|
|
84
|
+
/** True when `url` was created with `URL.createObjectURL` and must be revoked after use. */
|
|
85
|
+
revoke?: boolean;
|
|
27
86
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-DEz3mRLI.js","names":[],"sources":["../src/geometry/curls/types.ts"],"sourcesContent":["/**\n * Curl type identifiers and the deform contract. Kept free of the deform\n * implementations so the engine (and CSS-only builds) can reference the union and the\n * validation list without pulling the WebGL-only curl math into the eager bundle.\n */\nimport type { PageMesh } from './mesh';\n\n/** Where a corner-anchored curl folds from: y in [0,1], 0 = top edge, 1 = bottom edge. */\nexport interface CurlAnchor {\n y: number;\n /**\n * Full-width lone page (single-page mode). The leaf hinges at the container edge and\n * has no facing half to land on — deforms may soften and delay the flop so the curl\n * reads naturally before the renderer dissolves the sheet.\n */\n fill?: boolean;\n}\n\nexport type CurlDeform = (mesh: PageMesh, W: number, H: number, t: number, anchor: CurlAnchor) => void;\n\nexport interface CurlModel {\n readonly deform: CurlDeform;\n /** True when the fold originates at a corner (`anchor.y`) rather than the whole edge. */\n readonly anchored: boolean;\n /**\n * True when the sheet is flat by mid-turn, so a lone page can start dissolving earlier: there\n * is no curl left to read. Defaults to false, which suits a model still bent as it lands.\n */\n readonly flat?: boolean;\n /**\n * False to drop the specular highlight, for a sheet that never bends and so has no curve to\n * catch the light. Defaults to true.\n */\n readonly gloss?: boolean;\n}\n\n/**\n * The curl models carried in the bundle: the default, and the flat turn reduced motion falls\n * back to. Everything else is imported from `@zinejs/core/curls` and passed as a model, so a\n * book that never uses it never pays for it.\n */\nexport type CurlType = 'cone' | 'simple';\n\nexport const CURL_TYPES: CurlType[] = ['cone', 'simple'];\n\n/**\n * Curl models that exist but are not bundled. Named only so passing one as a string can say what\n * to do about it instead of failing as an unknown value.\n */\nexport const IMPORTABLE_CURLS = ['roll', 'leaf', 'flick', 'silk'];\n\nexport const DEFAULT_CURL: CurlType = 'cone';\n\n/** What the `curl` option accepts: a bundled name, or a model imported (or written) by the caller. */\nexport type CurlSpec = CurlType | CurlModel;\n"],"mappings":";AA2CA,IAAa,IAAyB,CAAC,QAAQ,QAAQ,GAM1C,IAAmB;CAAC;CAAQ;CAAQ;CAAS;AAAM,GAEnD,IAAyB"}
|