@kabel-project/kabel 1.0.7
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/(1.0.7)kabel.md +18 -0
- package/README.md +96 -0
- package/_READ_ME_MEDIA_/documentation/docs.md +293 -0
- package/_READ_ME_MEDIA_/workspace.png +0 -0
- package/comment-renderer/renderer.ts +228 -0
- package/controllers/base.ts +186 -0
- package/controllers/wasd.ts +132 -0
- package/docs/README.md +98 -0
- package/docs/_media/docs.md +289 -0
- package/docs/_media/index.html +168 -0
- package/docs/_media/workspace.png +0 -0
- package/docs/classes/CommentModel.md +271 -0
- package/docs/classes/CommentRenderer.md +457 -0
- package/docs/classes/ConnectableField.md +597 -0
- package/docs/classes/Connection.md +191 -0
- package/docs/classes/ContextMenuHTML.md +163 -0
- package/docs/classes/Coordinates.md +187 -0
- package/docs/classes/DropdownContainer.md +300 -0
- package/docs/classes/DummyField.md +393 -0
- package/docs/classes/Eventer.md +185 -0
- package/docs/classes/Field.md +461 -0
- package/docs/classes/InjectMsg.md +85 -0
- package/docs/classes/NodeSvg.md +1011 -0
- package/docs/classes/NumberField.md +559 -0
- package/docs/classes/OptConnectField.md +624 -0
- package/docs/classes/Renderer.md +1636 -0
- package/docs/classes/RendererConstants.md +343 -0
- package/docs/classes/Representer.md +95 -0
- package/docs/classes/RepresenterNode.md +175 -0
- package/docs/classes/TextField.md +559 -0
- package/docs/classes/Toolbox.md +172 -0
- package/docs/classes/WASDController.md +616 -0
- package/docs/classes/Widget.md +195 -0
- package/docs/classes/WorkspaceController.md +385 -0
- package/docs/classes/WorkspaceCoords.md +218 -0
- package/docs/classes/WorkspaceSvg.md +1380 -0
- package/docs/functions/clearMainWorkspace.md +20 -0
- package/docs/functions/getMainWorkspace.md +19 -0
- package/docs/functions/inject.md +35 -0
- package/docs/functions/setMainWorkspace.md +28 -0
- package/docs/globals.md +95 -0
- package/docs/interfaces/ColorStyle.md +43 -0
- package/docs/interfaces/ConnectorToFrom.md +57 -0
- package/docs/interfaces/DrawState.md +81 -0
- package/docs/interfaces/FieldConnectionData.md +25 -0
- package/docs/interfaces/FieldOptions.md +63 -0
- package/docs/interfaces/FieldRawBoxData.md +25 -0
- package/docs/interfaces/FieldVisualInfo.md +65 -0
- package/docs/interfaces/GridOptions.md +61 -0
- package/docs/interfaces/InjectOptions.md +133 -0
- package/docs/interfaces/InputFieldJson.md +50 -0
- package/docs/interfaces/KabelCommentRendering.md +31 -0
- package/docs/interfaces/KabelInterface.md +469 -0
- package/docs/interfaces/KabelNodeRendering.md +77 -0
- package/docs/interfaces/KabelUIX.md +105 -0
- package/docs/interfaces/KabelUtils.md +215 -0
- package/docs/interfaces/NodeEvents.md +42 -0
- package/docs/interfaces/NodeJson.md +104 -0
- package/docs/interfaces/NodePrototype.md +82 -0
- package/docs/interfaces/RegisteredEl.md +53 -0
- package/docs/interfaces/SerializedNode.md +128 -0
- package/docs/interfaces/TblxCategoryStruct.md +41 -0
- package/docs/interfaces/TblxFieldStruct.md +28 -0
- package/docs/interfaces/TblxNodeStruct.md +35 -0
- package/docs/interfaces/WidgetOptions.md +115 -0
- package/docs/interfaces/WidgetPrototypeList.md +15 -0
- package/docs/type-aliases/AnyField.md +13 -0
- package/docs/type-aliases/AnyFieldCls.md +13 -0
- package/docs/type-aliases/Color.md +13 -0
- package/docs/type-aliases/Connectable.md +13 -0
- package/docs/type-aliases/EventArgs.md +11 -0
- package/docs/type-aliases/EventSetupFn.md +25 -0
- package/docs/type-aliases/Hex.md +13 -0
- package/docs/type-aliases/RGBObject.md +37 -0
- package/docs/type-aliases/RGBString.md +13 -0
- package/docs/type-aliases/RGBTuple.md +13 -0
- package/docs/type-aliases/TblxObjStruct.md +52 -0
- package/docs/variables/CategoryColors.md +29 -0
- package/docs/variables/FieldMap.md +41 -0
- package/docs/variables/NodePrototypes.md +18 -0
- package/docs/variables/default.md +11 -0
- package/events/comment-drag-handle.ts +61 -0
- package/events/comment-input.ts +291 -0
- package/events/connection-line.ts +68 -0
- package/events/connector.ts +116 -0
- package/events/draggable.ts +119 -0
- package/events/events.ts +7 -0
- package/events/input-box.ts +213 -0
- package/events/node-x-btn.ts +25 -0
- package/index.d.ts +4 -0
- package/package.json +49 -0
- package/renderers/apollo/apollo.ts +21 -0
- package/renderers/apollo/constants.ts +40 -0
- package/renderers/apollo/renderer.ts +331 -0
- package/renderers/atlas/atlas.ts +15 -0
- package/renderers/constants.ts +87 -0
- package/renderers/renderer.ts +1288 -0
- package/renderers/representer-node.ts +52 -0
- package/renderers/representer.ts +25 -0
- package/src/category.ts +107 -0
- package/src/colors.ts +20 -0
- package/src/comment.ts +142 -0
- package/src/connection.ts +114 -0
- package/src/context-menu.ts +194 -0
- package/src/coordinates.ts +74 -0
- package/src/core.ts +202 -0
- package/src/ctx-menu-registry.ts +143 -0
- package/src/dropdown-menu.ts +215 -0
- package/src/field.ts +595 -0
- package/src/flyout.ts +165 -0
- package/src/fonts-manager.ts +38 -0
- package/src/grid.ts +162 -0
- package/src/headless-node.ts +27 -0
- package/src/index.ts +115 -0
- package/src/inject-headless.ts +18 -0
- package/src/inject.ts +213 -0
- package/src/main-workspace.ts +51 -0
- package/src/mutator.ts +40 -0
- package/src/node-types.ts +27 -0
- package/src/nodesvg.ts +756 -0
- package/src/prototypes.ts +9 -0
- package/src/renderer-map.ts +86 -0
- package/src/styles.css +224 -0
- package/src/toolbox.ts +125 -0
- package/src/types.ts +205 -0
- package/src/undo-redo.ts +87 -0
- package/src/visual-types.ts +29 -0
- package/src/widget-prototypes.ts +11 -0
- package/src/widget.ts +139 -0
- package/src/workspace-coords.ts +14 -0
- package/src/workspace-svg.ts +736 -0
- package/src/workspace.ts +155 -0
- package/test-server.js +61 -0
- package/themes/dark.ts +32 -0
- package/themes/default.ts +28 -0
- package/themes/themes.ts +9 -0
- package/tsconfig.json +25 -0
- package/typedoc.json +10 -0
- package/util/emitter.ts +33 -0
- package/util/env.ts +11 -0
- package/util/escape-html.ts +22 -0
- package/util/eventer.ts +108 -0
- package/util/has-prop.ts +4 -0
- package/util/parse-color.ts +42 -0
- package/util/path.ts +99 -0
- package/util/styler.ts +41 -0
- package/util/uid.ts +184 -0
- package/util/unescape-html.ts +22 -0
- package/util/user-state.ts +68 -0
- package/util/wait-anim-frames.ts +24 -0
- package/util/window-listeners.ts +62 -0
- package/webpack.config.js +80 -0
package/util/uid.ts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// uid.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Strategy used to generate UIDs.
|
|
5
|
+
*/
|
|
6
|
+
export type UIDStrategy = "uuidv4" | "ulid" | "nanoid" | "short";
|
|
7
|
+
|
|
8
|
+
export interface UIDOptions {
|
|
9
|
+
/** For "nanoid": length of the id (default 21) */
|
|
10
|
+
size?: number;
|
|
11
|
+
/** For "nanoid": custom alphabet (default URL-safe) */
|
|
12
|
+
alphabet?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Web Crypto shim (browser + Node 16+) */
|
|
16
|
+
const cryptoAPI: Crypto | null =
|
|
17
|
+
(typeof globalThis !== "undefined" && (globalThis as any).crypto) || null;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Returns a Uint8Array of random bytes.
|
|
21
|
+
* Uses Web Crypto if available; falls back to Math.random otherwise (not cryptographically secure).
|
|
22
|
+
*
|
|
23
|
+
* @param {number} len - Number of random bytes to generate.
|
|
24
|
+
* @returns {Uint8Array} Random bytes array.
|
|
25
|
+
*/
|
|
26
|
+
function randBytes(len: number): Uint8Array {
|
|
27
|
+
if (cryptoAPI?.getRandomValues) {
|
|
28
|
+
const buf = new Uint8Array(len);
|
|
29
|
+
cryptoAPI.getRandomValues(buf);
|
|
30
|
+
return buf;
|
|
31
|
+
}
|
|
32
|
+
// Last resort (very old envs). Not cryptographically strong.
|
|
33
|
+
const buf = new Uint8Array(len);
|
|
34
|
+
for (let i = 0; i < len; i++) buf[i] = Math.floor(Math.random() * 256);
|
|
35
|
+
return buf;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generates an RFC4122-compliant UUID v4.
|
|
40
|
+
* Uses `crypto.randomUUID` if available.
|
|
41
|
+
*
|
|
42
|
+
* @returns {string} UUID v4 string.
|
|
43
|
+
*/
|
|
44
|
+
export function uuidv4(): string {
|
|
45
|
+
const g: any = globalThis as any;
|
|
46
|
+
if (g?.crypto?.randomUUID) return g.crypto.randomUUID();
|
|
47
|
+
|
|
48
|
+
const b = randBytes(16);
|
|
49
|
+
// Per RFC: set version + variant bits
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
b[6] = (b[6] & 0x0f) | 0x40;
|
|
52
|
+
// @ts-ignore
|
|
53
|
+
b[8] = (b[8] & 0x3f) | 0x80;
|
|
54
|
+
|
|
55
|
+
const hex: string[] = [];
|
|
56
|
+
for (let i = 0; i < 256; i++) hex.push(i.toString(16).padStart(2, "0"));
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
// @ts-ignore
|
|
60
|
+
hex[b[0]] + hex[b[1]] + hex[b[2]] + hex[b[3]] + "-" +
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
hex[b[4]] + hex[b[5]] + "-" +
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
hex[b[6]] + hex[b[7]] + "-" +
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
hex[b[8]] + hex[b[9]] + "-" +
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
hex[b[10]] + hex[b[11]] + hex[b[12]] + hex[b[13]] + hex[b[14]] + hex[b[15]]
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Crockford Base32 alphabet for ULID */
|
|
73
|
+
const CROCK32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; // no I L O U
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generates a 26-character ULID (time-sortable unique ID).
|
|
77
|
+
*
|
|
78
|
+
* @param {number} [date] - Optional timestamp in milliseconds. Defaults to `Date.now()`.
|
|
79
|
+
* @returns {string} ULID string.
|
|
80
|
+
*/
|
|
81
|
+
export function ulid(date?: number): string {
|
|
82
|
+
const time = (typeof date === "number" ? date : Date.now()) >>> 0; // low 32 bits
|
|
83
|
+
const timeHi = Math.floor((typeof date === "number" ? date : Date.now()) / 0x100000000) >>> 0; // high 32
|
|
84
|
+
// ULID uses 48-bit time; do base32 encode 48 bits
|
|
85
|
+
const time48 = new Uint8Array(6);
|
|
86
|
+
// write 48-bit big-endian
|
|
87
|
+
let t = BigInt(typeof date === "number" ? date : Date.now());
|
|
88
|
+
for (let i = 5; i >= 0; i--) {
|
|
89
|
+
time48[i] = Number(t & 0xffn);
|
|
90
|
+
t >>= 8n;
|
|
91
|
+
}
|
|
92
|
+
// first 10 chars = time
|
|
93
|
+
let out = "";
|
|
94
|
+
let acc = 0;
|
|
95
|
+
let bits = 0;
|
|
96
|
+
for (let i = 0; i < 6; i++) {
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
acc = (acc << 8) | time48[i];
|
|
99
|
+
bits += 8;
|
|
100
|
+
while (bits >= 5) {
|
|
101
|
+
bits -= 5;
|
|
102
|
+
out += CROCK32[(acc >>> bits) & 31];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (bits > 0) out += CROCK32[(acc << (5 - bits)) & 31];
|
|
106
|
+
out = out.slice(0, 10);
|
|
107
|
+
|
|
108
|
+
// last 16 chars = randomness (80 bits)
|
|
109
|
+
const rnd = randBytes(10);
|
|
110
|
+
acc = 0; bits = 0;
|
|
111
|
+
for (let i = 0; i < rnd.length; i++) {
|
|
112
|
+
// @ts-ignore
|
|
113
|
+
acc = (acc << 8) | rnd[i];
|
|
114
|
+
bits += 8;
|
|
115
|
+
while (bits >= 5) {
|
|
116
|
+
bits -= 5;
|
|
117
|
+
out += CROCK32[(acc >>> bits) & 31];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (bits > 0) out += CROCK32[(acc << (5 - bits)) & 31];
|
|
121
|
+
|
|
122
|
+
return out.slice(0, 26);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** NanoID-style: URL-safe alphabet by default, configurable length/alphabet */
|
|
126
|
+
const DEFAULT_ALPHABET =
|
|
127
|
+
"_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
128
|
+
/**
|
|
129
|
+
* Generates a NanoID-style string.
|
|
130
|
+
*
|
|
131
|
+
* @param {number} [size=21] - Length of the generated ID.
|
|
132
|
+
* @param {string} [alphabet=DEFAULT_ALPHABET] - Alphabet to use.
|
|
133
|
+
* @returns {string} Generated NanoID string.
|
|
134
|
+
*/
|
|
135
|
+
export function nanoid(size: number = 21, alphabet: string = DEFAULT_ALPHABET): string {
|
|
136
|
+
if (size <= 0) throw new Error("size must be > 0");
|
|
137
|
+
const mask = (1 << Math.ceil(Math.log2(alphabet.length))) - 1;
|
|
138
|
+
let id = "";
|
|
139
|
+
while (id.length < size) {
|
|
140
|
+
const bytes = randBytes(1);
|
|
141
|
+
// @ts-ignore
|
|
142
|
+
const idx = bytes[0] & mask;
|
|
143
|
+
if (idx < alphabet.length) id += alphabet[idx];
|
|
144
|
+
}
|
|
145
|
+
return id;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Generates a short, mostly-unique ID (timestamp + counter + random).
|
|
150
|
+
* Not cryptographically strong.
|
|
151
|
+
*
|
|
152
|
+
* @returns {string} Short UID string.
|
|
153
|
+
*/
|
|
154
|
+
let _ctr = 0;
|
|
155
|
+
export function shortId(): string {
|
|
156
|
+
const ts = Date.now().toString(36);
|
|
157
|
+
_ctr = (_ctr + 1) & 0xfff; // 0..4095
|
|
158
|
+
const c = _ctr.toString(36).padStart(3, "0");
|
|
159
|
+
const r = Array.from(randBytes(3))
|
|
160
|
+
.map(b => (b & 0x3f).toString(36).padStart(2, "0"))
|
|
161
|
+
.join("")
|
|
162
|
+
.slice(0, 4);
|
|
163
|
+
return `${ts}${c}${r}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Generates a UID using the specified strategy.
|
|
168
|
+
*
|
|
169
|
+
* @param {UIDStrategy} [strategy="uuidv4"] - The UID strategy to use.
|
|
170
|
+
* @param {UIDOptions} [opts={}] - Options for the selected strategy.
|
|
171
|
+
* @returns {string} Generated UID.
|
|
172
|
+
*/
|
|
173
|
+
export function generateUID(strategy: UIDStrategy = "uuidv4", opts: UIDOptions = {}): string {
|
|
174
|
+
switch (strategy) {
|
|
175
|
+
case "uuidv4": return uuidv4();
|
|
176
|
+
case "ulid": return ulid();
|
|
177
|
+
case "nanoid": return nanoid(opts.size ?? 21, opts.alphabet ?? DEFAULT_ALPHABET);
|
|
178
|
+
case "short": return shortId();
|
|
179
|
+
default: {
|
|
180
|
+
const _exhaustive: never = strategy;
|
|
181
|
+
return uuidv4();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts HTML-escaped characters in a string back to their literal form.
|
|
3
|
+
*
|
|
4
|
+
* Specifically, it replaces:
|
|
5
|
+
* - `<` with `<`
|
|
6
|
+
* - `>` with `>`
|
|
7
|
+
* - `"` with `"`
|
|
8
|
+
* - `'` with `'`
|
|
9
|
+
* - `&` with `&`
|
|
10
|
+
*
|
|
11
|
+
* @param {string} s - The string containing HTML-escaped characters.
|
|
12
|
+
* @returns {string} The unescaped string with HTML entities replaced by their literal characters.
|
|
13
|
+
*/
|
|
14
|
+
function unescapeAttr(s: string): string {
|
|
15
|
+
return s.replace(/</g, "<")
|
|
16
|
+
.replace(/>/g, ">")
|
|
17
|
+
.replace(/"/g, '"')
|
|
18
|
+
.replace(/'/g, "'")
|
|
19
|
+
.replace(/&/g, "&");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default unescapeAttr;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
type StateChangeCallback = (addedOrRemoved: 0 | 1) => void;
|
|
2
|
+
|
|
3
|
+
class UserState {
|
|
4
|
+
private state: Set<string>;
|
|
5
|
+
private callbacks: Map<string, StateChangeCallback[]>;
|
|
6
|
+
/**
|
|
7
|
+
* Stores the user's current UIX state
|
|
8
|
+
* Ex: 'typing'
|
|
9
|
+
*/
|
|
10
|
+
constructor() {
|
|
11
|
+
this.state = new Set();
|
|
12
|
+
this.callbacks = new Map();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Adds a state
|
|
17
|
+
* @param name - The name of the state (ex: 'typing')
|
|
18
|
+
*/
|
|
19
|
+
setState(name: string) {
|
|
20
|
+
const wasPresent = this.state.has(name);
|
|
21
|
+
if (!wasPresent) {
|
|
22
|
+
this.state.add(name);
|
|
23
|
+
this.triggerCallbacks(name, 1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Removes a state
|
|
29
|
+
* @param name - The name of the state (ex: 'typing')
|
|
30
|
+
*/
|
|
31
|
+
removeState(name: string) {
|
|
32
|
+
const wasPresent = this.state.has(name);
|
|
33
|
+
if (wasPresent) {
|
|
34
|
+
this.state.delete(name);
|
|
35
|
+
this.triggerCallbacks(name, 0);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Checks if a state is active
|
|
41
|
+
* @param name - The name of the state (ex: 'typing')
|
|
42
|
+
*/
|
|
43
|
+
hasState(name: string) {
|
|
44
|
+
return this.state.has(name);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Registers a callback for state changes
|
|
49
|
+
* @param name - Name of the state to check changes for.
|
|
50
|
+
* @param cb - Callback to call on state change.
|
|
51
|
+
*/
|
|
52
|
+
onStateChange(name: string, cb: StateChangeCallback) {
|
|
53
|
+
if (!this.callbacks.has(name)) this.callbacks.set(name, []);
|
|
54
|
+
this.callbacks.get(name)!.push(cb);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Internal: triggers callbacks for a state */
|
|
58
|
+
private triggerCallbacks(name: string, addedOrRemoved: 0 | 1) {
|
|
59
|
+
const cbs = this.callbacks.get(name);
|
|
60
|
+
if (!cbs) return;
|
|
61
|
+
for (const cb of cbs) cb(addedOrRemoved);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const userState = new UserState();
|
|
65
|
+
export default userState;
|
|
66
|
+
export {
|
|
67
|
+
UserState
|
|
68
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delays execution of a callback by a specified number of animation frames.
|
|
3
|
+
*
|
|
4
|
+
* Uses `requestAnimationFrame` to count frames, then calls the provided callback.
|
|
5
|
+
*
|
|
6
|
+
* @param {number} frames - The number of animation frames to wait before executing the callback.
|
|
7
|
+
* @param {() => void} callback - The function to execute after the specified frames have passed.
|
|
8
|
+
*/
|
|
9
|
+
function waitFrames(frames: number, callback: () => void): void {
|
|
10
|
+
let count = 0;
|
|
11
|
+
|
|
12
|
+
function step() {
|
|
13
|
+
count++;
|
|
14
|
+
if (count >= frames) {
|
|
15
|
+
callback();
|
|
16
|
+
} else {
|
|
17
|
+
requestAnimationFrame(step);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
requestAnimationFrame(step);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default waitFrames;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
type WindowEventKeys =
|
|
2
|
+
| 'resize'
|
|
3
|
+
| 'scroll'
|
|
4
|
+
| 'blur'
|
|
5
|
+
| 'focus'
|
|
6
|
+
| 'visibilitychange'
|
|
7
|
+
| 'pointerlockchange'
|
|
8
|
+
| 'beforeunload';
|
|
9
|
+
|
|
10
|
+
type WindowListenersMap = Record<WindowEventKeys, Array<(event: Event) => void>>;
|
|
11
|
+
|
|
12
|
+
const windowListeners: WindowListenersMap = {
|
|
13
|
+
resize: [],
|
|
14
|
+
scroll: [],
|
|
15
|
+
blur: [],
|
|
16
|
+
focus: [],
|
|
17
|
+
visibilitychange: [],
|
|
18
|
+
pointerlockchange: [],
|
|
19
|
+
beforeunload: [],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function handleEvent(type: WindowEventKeys, event: Event) {
|
|
23
|
+
const list = windowListeners[type];
|
|
24
|
+
if (!list.length) return;
|
|
25
|
+
for (const fn of list) {
|
|
26
|
+
try {
|
|
27
|
+
fn(event);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.error(`[Kabel] Error in window listener for '${type}':`, err);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// auto attach
|
|
35
|
+
(Object.keys(windowListeners) as WindowEventKeys[]).forEach((type) => {
|
|
36
|
+
window.addEventListener(type, (e) => handleEvent(type, e));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export function addWindowListener(
|
|
40
|
+
type: WindowEventKeys,
|
|
41
|
+
fn: (event: Event) => void
|
|
42
|
+
) {
|
|
43
|
+
windowListeners[type].push(fn);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function removeWindowListener(
|
|
47
|
+
type: WindowEventKeys,
|
|
48
|
+
fn: (event: Event) => void
|
|
49
|
+
) {
|
|
50
|
+
const list = windowListeners[type];
|
|
51
|
+
const i = list.indexOf(fn);
|
|
52
|
+
if (i !== -1) list.splice(i, 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function clearWindowListeners(type?: WindowEventKeys) {
|
|
56
|
+
if (type) windowListeners[type].length = 0;
|
|
57
|
+
else (Object.keys(windowListeners) as WindowEventKeys[]).forEach((k) => {
|
|
58
|
+
windowListeners[k].length = 0;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default windowListeners;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const TerserPlugin = require('terser-webpack-plugin');
|
|
3
|
+
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
mode: 'production', // <- production enables lots of optimizations automatically
|
|
7
|
+
entry: './src/index.ts',
|
|
8
|
+
output: {
|
|
9
|
+
filename: 'kabel.js',
|
|
10
|
+
path: path.resolve(__dirname, 'dist'),
|
|
11
|
+
library: {
|
|
12
|
+
name: 'Kabel',
|
|
13
|
+
type: 'umd',
|
|
14
|
+
},
|
|
15
|
+
globalObject: 'this',
|
|
16
|
+
libraryExport: 'default',
|
|
17
|
+
clean: {
|
|
18
|
+
keep: /index\.html/,
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
resolve: {
|
|
22
|
+
extensions: ['.ts', '.js'],
|
|
23
|
+
},
|
|
24
|
+
module: {
|
|
25
|
+
rules: [
|
|
26
|
+
{
|
|
27
|
+
test: /\.ts$/,
|
|
28
|
+
use: 'ts-loader',
|
|
29
|
+
exclude: /node_modules/,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
enforce: 'pre',
|
|
33
|
+
test: /\.js$/,
|
|
34
|
+
loader: 'source-map-loader',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
test: /\.(css|svg)$/,
|
|
38
|
+
use: 'raw-loader'
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
optimization: {
|
|
43
|
+
minimize: true,
|
|
44
|
+
minimizer: [
|
|
45
|
+
new TerserPlugin({
|
|
46
|
+
terserOptions: {
|
|
47
|
+
compress: {
|
|
48
|
+
//drop_console: true, // kill console.logs
|
|
49
|
+
drop_debugger: true, // kill debugger;
|
|
50
|
+
pure_funcs: ['console.info', 'console.debug'],
|
|
51
|
+
// aggressive compress flags
|
|
52
|
+
booleans_as_integers: true,
|
|
53
|
+
hoist_funs: true,
|
|
54
|
+
passes: 3,
|
|
55
|
+
collapse_vars: true, // inline single-use vars
|
|
56
|
+
reduce_vars: true, // further optimizes var usage
|
|
57
|
+
join_vars: true, // join consecutive var statements
|
|
58
|
+
keep_fargs: false, // remove unused function args
|
|
59
|
+
if_return: true, // optimize if/return statements
|
|
60
|
+
conditionals: true, // convert if-else to conditional expressions
|
|
61
|
+
sequences: true, // join consecutive statements with comma
|
|
62
|
+
unused: true, // remove unused vars/functions
|
|
63
|
+
loops: true, // optimize loops
|
|
64
|
+
side_effects: true, // remove expressions without side-effects
|
|
65
|
+
},
|
|
66
|
+
mangle: {
|
|
67
|
+
//toplevel: false, // <- don’t scramble top-level/public names
|
|
68
|
+
},
|
|
69
|
+
format: {
|
|
70
|
+
comments: false,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
extractComments: false,
|
|
74
|
+
parallel: true,
|
|
75
|
+
}),
|
|
76
|
+
new CssMinimizerPlugin(),
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
devtool: 'source-map',
|
|
80
|
+
};
|