@polymorfa/devtools 0.1.0-dev.20260922094209
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/LICENSE +21 -0
- package/README.md +41 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/model.d.ts +44 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +79 -0
- package/dist/network.d.ts +9 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +26 -0
- package/dist/panel.d.ts +31 -0
- package/dist/panel.d.ts.map +1 -0
- package/dist/panel.js +407 -0
- package/dist/production.d.ts +3 -0
- package/dist/production.d.ts.map +1 -0
- package/dist/production.js +5 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Polymorfa
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# `@polymorfa/devtools`
|
|
2
|
+
|
|
3
|
+
An explicit development assistant for testing Polymorfa appearance, direction,
|
|
4
|
+
motion, viewport presets, network profiles, and redacted request diagnostics.
|
|
5
|
+
|
|
6
|
+
The assistant only enables when a trusted build environment and the client-token
|
|
7
|
+
environment match and both are non-production. URL query parameters are never
|
|
8
|
+
consulted. The `@polymorfa/devtools/production` entry point is inert.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { DevAssistant, mountDevAssistant } from "@polymorfa/devtools";
|
|
12
|
+
|
|
13
|
+
const assistant = new DevAssistant({
|
|
14
|
+
environment: "development",
|
|
15
|
+
tokenEnvironment: "development",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
mountDevAssistant(assistant);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The assistant starts as a 40px launcher button in the bottom-left corner, so
|
|
22
|
+
it stays clear of a chat composer. Selecting it opens the panel beside it;
|
|
23
|
+
Escape or the close button closes the panel and returns focus to the
|
|
24
|
+
launcher. The open state is kept in `sessionStorage` for the tab when
|
|
25
|
+
storage is available. The launcher and panel render in a shadow root, follow
|
|
26
|
+
the system light or dark color scheme, and skip their transitions when
|
|
27
|
+
reduced motion is preferred.
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
const mounted = mountDevAssistant(assistant, {
|
|
31
|
+
position: "top-right", // "bottom-left" (default), "bottom-right", "top-left"
|
|
32
|
+
defaultOpen: false,
|
|
33
|
+
offset: { x: 16, y: 16 },
|
|
34
|
+
parent: document.body,
|
|
35
|
+
});
|
|
36
|
+
mounted?.open();
|
|
37
|
+
mounted?.dispose();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Passing a parent element as the second argument still works. The returned
|
|
41
|
+
`element` is the host; the controls are inside its open `shadowRoot`.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { DevAssistant, type DevAssistantOptions, type DevAssistantSettings, type DevAssistantSnapshot, type NetworkProfile, type RuntimeEnvironment, type ViewportPreset, } from "./model.js";
|
|
2
|
+
export { createSimulatedFetch, type SimulatedFetchOptions } from "./network.js";
|
|
3
|
+
export { mountDevAssistant, type DevAssistantPosition, type MountDevAssistantOptions, type MountedDevAssistant, } from "./panel.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EACL,iBAAiB,EACjB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { BrowserDiagnosticEvent } from "@polymorfa/browser";
|
|
2
|
+
import { type Appearance, type AppearanceInput, type Direction, type MotionPreference, type Theme } from "@polymorfa/ui";
|
|
3
|
+
export type RuntimeEnvironment = "development" | "test" | "preview" | "production";
|
|
4
|
+
export type NetworkProfile = "online" | "slow" | "offline";
|
|
5
|
+
export type ViewportPreset = "responsive" | "mobile" | "tablet" | "desktop";
|
|
6
|
+
export interface DevAssistantSettings {
|
|
7
|
+
readonly appearance: Appearance;
|
|
8
|
+
readonly locale: string;
|
|
9
|
+
readonly direction: Direction;
|
|
10
|
+
readonly motion: MotionPreference;
|
|
11
|
+
readonly viewport: ViewportPreset;
|
|
12
|
+
readonly network: NetworkProfile;
|
|
13
|
+
}
|
|
14
|
+
export interface DevAssistantOptions {
|
|
15
|
+
/** Must come from trusted build or server configuration, never the URL. */
|
|
16
|
+
readonly environment: RuntimeEnvironment;
|
|
17
|
+
/** Environment asserted by the client-token minting service. */
|
|
18
|
+
readonly tokenEnvironment: RuntimeEnvironment;
|
|
19
|
+
readonly appearance?: AppearanceInput;
|
|
20
|
+
readonly locale?: string;
|
|
21
|
+
readonly maxEvents?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface DevAssistantSnapshot extends DevAssistantSettings {
|
|
24
|
+
readonly enabled: boolean;
|
|
25
|
+
readonly events: readonly unknown[];
|
|
26
|
+
}
|
|
27
|
+
export declare class DevAssistant {
|
|
28
|
+
#private;
|
|
29
|
+
constructor(options: DevAssistantOptions);
|
|
30
|
+
getSnapshot: () => DevAssistantSnapshot;
|
|
31
|
+
subscribe: (listener: () => void) => (() => void);
|
|
32
|
+
diagnosticSink: (event: BrowserDiagnosticEvent) => void;
|
|
33
|
+
configure(input: {
|
|
34
|
+
readonly theme?: Theme;
|
|
35
|
+
readonly locale?: string;
|
|
36
|
+
readonly direction?: Direction;
|
|
37
|
+
readonly motion?: MotionPreference;
|
|
38
|
+
readonly viewport?: ViewportPreset;
|
|
39
|
+
readonly network?: NetworkProfile;
|
|
40
|
+
}): void;
|
|
41
|
+
copyableConfig(): string;
|
|
42
|
+
clearEvents(): void;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,KAAK,EACX,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,kBAAkB,GAC5B,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,CAAC;AACpD,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAC3D,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,EAAE,kBAAkB,CAAC;IACzC,gEAAgE;IAChE,QAAQ,CAAC,gBAAgB,EAAE,kBAAkB,CAAC;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAChE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;CACrC;AAED,qBAAa,YAAY;;gBAOX,OAAO,EAAE,mBAAmB;IAiBxC,WAAW,QAAO,oBAAoB,CAKjC;IAEL,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAG9C;IAEF,cAAc,GAAI,OAAO,sBAAsB,KAAG,IAAI,CAMpD;IAEF,SAAS,CAAC,KAAK,EAAE;QACf,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;QACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;QAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;QACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;QACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;KACnC,GAAG,IAAI;IA2BR,cAAc,IAAI,MAAM;IAIxB,WAAW,IAAI,IAAI;CASpB"}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { defineAppearance, redactDiagnostic, } from "@polymorfa/ui";
|
|
2
|
+
export class DevAssistant {
|
|
3
|
+
#enabled;
|
|
4
|
+
#maxEvents;
|
|
5
|
+
#listeners = new Set();
|
|
6
|
+
#settings;
|
|
7
|
+
#events = [];
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.#enabled =
|
|
10
|
+
options.environment !== "production" &&
|
|
11
|
+
options.tokenEnvironment !== "production" &&
|
|
12
|
+
options.environment === options.tokenEnvironment;
|
|
13
|
+
this.#maxEvents = Math.max(1, Math.floor(options.maxEvents ?? 50));
|
|
14
|
+
const appearance = defineAppearance(options.appearance);
|
|
15
|
+
this.#settings = Object.freeze({
|
|
16
|
+
appearance,
|
|
17
|
+
locale: options.locale ?? "en",
|
|
18
|
+
direction: appearance.layout.direction,
|
|
19
|
+
motion: appearance.variables.motion,
|
|
20
|
+
viewport: "responsive",
|
|
21
|
+
network: "online",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
getSnapshot = () => Object.freeze({
|
|
25
|
+
...this.#settings,
|
|
26
|
+
enabled: this.#enabled,
|
|
27
|
+
events: Object.freeze([...this.#events]),
|
|
28
|
+
});
|
|
29
|
+
subscribe = (listener) => {
|
|
30
|
+
this.#listeners.add(listener);
|
|
31
|
+
return () => this.#listeners.delete(listener);
|
|
32
|
+
};
|
|
33
|
+
diagnosticSink = (event) => {
|
|
34
|
+
if (!this.#enabled)
|
|
35
|
+
return;
|
|
36
|
+
this.#events = [...this.#events, redactDiagnostic(event)].slice(-this.#maxEvents);
|
|
37
|
+
this.#emit();
|
|
38
|
+
};
|
|
39
|
+
configure(input) {
|
|
40
|
+
if (!this.#enabled)
|
|
41
|
+
return;
|
|
42
|
+
const appearance = defineAppearance({
|
|
43
|
+
...this.#settings.appearance,
|
|
44
|
+
...(input.theme === undefined ? {} : { theme: input.theme }),
|
|
45
|
+
variables: {
|
|
46
|
+
...this.#settings.appearance.variables,
|
|
47
|
+
...(input.motion === undefined ? {} : { motion: input.motion }),
|
|
48
|
+
},
|
|
49
|
+
layout: {
|
|
50
|
+
...this.#settings.appearance.layout,
|
|
51
|
+
...(input.direction === undefined
|
|
52
|
+
? {}
|
|
53
|
+
: { direction: input.direction }),
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
this.#settings = Object.freeze({
|
|
57
|
+
appearance,
|
|
58
|
+
locale: input.locale ?? this.#settings.locale,
|
|
59
|
+
direction: input.direction ?? this.#settings.direction,
|
|
60
|
+
motion: input.motion ?? this.#settings.motion,
|
|
61
|
+
viewport: input.viewport ?? this.#settings.viewport,
|
|
62
|
+
network: input.network ?? this.#settings.network,
|
|
63
|
+
});
|
|
64
|
+
this.#emit();
|
|
65
|
+
}
|
|
66
|
+
copyableConfig() {
|
|
67
|
+
return JSON.stringify(this.#settings, null, 2);
|
|
68
|
+
}
|
|
69
|
+
clearEvents() {
|
|
70
|
+
if (!this.#enabled)
|
|
71
|
+
return;
|
|
72
|
+
this.#events = [];
|
|
73
|
+
this.#emit();
|
|
74
|
+
}
|
|
75
|
+
#emit() {
|
|
76
|
+
for (const listener of this.#listeners)
|
|
77
|
+
listener();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NetworkProfile } from "./model.js";
|
|
2
|
+
export interface SimulatedFetchOptions {
|
|
3
|
+
readonly profile: () => NetworkProfile;
|
|
4
|
+
readonly delayMs?: number;
|
|
5
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
6
|
+
}
|
|
7
|
+
/** Explicitly wraps fetch for development; no simulation is installed globally. */
|
|
8
|
+
export declare function createSimulatedFetch(options: SimulatedFetchOptions): typeof fetch;
|
|
9
|
+
//# sourceMappingURL=network.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,cAAc,CAAC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAC1C;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,KAAK,CAWd"}
|
package/dist/network.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Explicitly wraps fetch for development; no simulation is installed globally. */
|
|
2
|
+
export function createSimulatedFetch(options) {
|
|
3
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
4
|
+
return async (input, init) => {
|
|
5
|
+
const profile = options.profile();
|
|
6
|
+
if (profile === "offline")
|
|
7
|
+
throw new TypeError("Simulated offline network.");
|
|
8
|
+
if (profile === "slow") {
|
|
9
|
+
await abortableDelay(options.delayMs ?? 800, init?.signal);
|
|
10
|
+
}
|
|
11
|
+
return fetchImplementation(input, init);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function abortableDelay(delayMs, signal) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
if (signal?.aborted === true) {
|
|
17
|
+
reject(signal.reason);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const timeout = setTimeout(resolve, delayMs);
|
|
21
|
+
signal?.addEventListener("abort", () => {
|
|
22
|
+
clearTimeout(timeout);
|
|
23
|
+
reject(signal.reason);
|
|
24
|
+
}, { once: true });
|
|
25
|
+
});
|
|
26
|
+
}
|
package/dist/panel.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DevAssistant } from "./model.js";
|
|
2
|
+
export type DevAssistantPosition = "bottom-left" | "bottom-right" | "top-left" | "top-right";
|
|
3
|
+
export interface MountDevAssistantOptions {
|
|
4
|
+
/** Corner for the launcher. Defaults to `"bottom-left"`. */
|
|
5
|
+
readonly position?: DevAssistantPosition;
|
|
6
|
+
/** Open the panel on first mount. Defaults to `false`. */
|
|
7
|
+
readonly defaultOpen?: boolean;
|
|
8
|
+
/** Distance from the viewport edges in pixels. Defaults to 16 each. */
|
|
9
|
+
readonly offset?: {
|
|
10
|
+
readonly x?: number;
|
|
11
|
+
readonly y?: number;
|
|
12
|
+
};
|
|
13
|
+
/** Element to mount into. Defaults to `document.body`. */
|
|
14
|
+
readonly parent?: HTMLElement;
|
|
15
|
+
}
|
|
16
|
+
export interface MountedDevAssistant {
|
|
17
|
+
/** Host element; the launcher and panel live in its open shadow root. */
|
|
18
|
+
readonly element: HTMLElement;
|
|
19
|
+
readonly isOpen: boolean;
|
|
20
|
+
open(): void;
|
|
21
|
+
close(): void;
|
|
22
|
+
toggle(): void;
|
|
23
|
+
dispose(): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Mount the development panel: a small launcher in a corner that opens the
|
|
27
|
+
* panel next to it. Returns `null` when the assistant is disabled. The
|
|
28
|
+
* second argument may also be the parent element, as in earlier versions.
|
|
29
|
+
*/
|
|
30
|
+
export declare function mountDevAssistant(assistant: DevAssistant, options?: MountDevAssistantOptions | HTMLElement): MountedDevAssistant | null;
|
|
31
|
+
//# sourceMappingURL=panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../src/panel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAwB,MAAM,YAAY,CAAC;AAErE,MAAM,MAAM,oBAAoB,GAC9B,aAAa,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,CAAC;AAE5D,MAAM,WAAW,wBAAwB;IACvC,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IACzC,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/D,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;CACjB;AAkND;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,YAAY,EACvB,OAAO,GAAE,wBAAwB,GAAG,WAAgB,GACnD,mBAAmB,GAAG,IAAI,CA+K5B"}
|
package/dist/panel.js
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
const STORAGE_KEY = "polymorfa:devtools:open";
|
|
2
|
+
function readOpen() {
|
|
3
|
+
try {
|
|
4
|
+
const value = globalThis.sessionStorage?.getItem(STORAGE_KEY);
|
|
5
|
+
return value === "1" ? true : value === "0" ? false : undefined;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function writeOpen(open) {
|
|
12
|
+
try {
|
|
13
|
+
globalThis.sessionStorage?.setItem(STORAGE_KEY, open ? "1" : "0");
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// Storage can be blocked; the state then lasts for this page only.
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const STYLES = `
|
|
20
|
+
:host {
|
|
21
|
+
all: initial;
|
|
22
|
+
--bg: #ffffff;
|
|
23
|
+
--fg: #17151f;
|
|
24
|
+
--muted: #6d6878;
|
|
25
|
+
--border: #e4e1ea;
|
|
26
|
+
--surface: #f5f4f8;
|
|
27
|
+
--accent: #5b4bf7;
|
|
28
|
+
--on-accent: #ffffff;
|
|
29
|
+
--shadow: 0 24px 60px rgb(23 21 31 / 0.18), 0 2px 8px rgb(23 21 31 / 0.08);
|
|
30
|
+
position: fixed;
|
|
31
|
+
z-index: 2147483647;
|
|
32
|
+
font: 13px/1.45 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
33
|
+
color: var(--fg);
|
|
34
|
+
color-scheme: light;
|
|
35
|
+
}
|
|
36
|
+
@media (prefers-color-scheme: dark) {
|
|
37
|
+
:host {
|
|
38
|
+
--bg: #1c1b22;
|
|
39
|
+
--fg: #f1eff6;
|
|
40
|
+
--muted: #a19cae;
|
|
41
|
+
--border: #34313e;
|
|
42
|
+
--surface: #26242e;
|
|
43
|
+
--accent: #8b80ff;
|
|
44
|
+
--on-accent: #120f2b;
|
|
45
|
+
--shadow: 0 24px 60px rgb(0 0 0 / 0.5), 0 2px 8px rgb(0 0 0 / 0.3);
|
|
46
|
+
color-scheme: dark;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
50
|
+
button, select { font: inherit; color: inherit; }
|
|
51
|
+
.launcher {
|
|
52
|
+
display: grid;
|
|
53
|
+
place-items: center;
|
|
54
|
+
width: 40px;
|
|
55
|
+
height: 40px;
|
|
56
|
+
padding: 0;
|
|
57
|
+
border: 1px solid var(--border);
|
|
58
|
+
border-radius: 50%;
|
|
59
|
+
background: var(--bg);
|
|
60
|
+
color: var(--accent);
|
|
61
|
+
box-shadow: 0 4px 14px rgb(0 0 0 / 0.16);
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
opacity: 0.85;
|
|
64
|
+
transition: opacity 120ms ease, transform 120ms ease, background-color 120ms ease;
|
|
65
|
+
}
|
|
66
|
+
.launcher:hover, .launcher[aria-expanded="true"] { opacity: 1; }
|
|
67
|
+
.launcher[aria-expanded="true"] { background: var(--accent); color: var(--on-accent); border-color: transparent; }
|
|
68
|
+
.launcher:active { transform: scale(0.94); }
|
|
69
|
+
.launcher svg { width: 20px; height: 20px; }
|
|
70
|
+
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
71
|
+
.panel {
|
|
72
|
+
position: absolute;
|
|
73
|
+
width: min(320px, calc(100vw - 32px));
|
|
74
|
+
max-height: min(560px, calc(100vh - 88px));
|
|
75
|
+
overflow: auto;
|
|
76
|
+
border: 1px solid var(--border);
|
|
77
|
+
border-radius: 14px;
|
|
78
|
+
background: var(--bg);
|
|
79
|
+
box-shadow: var(--shadow);
|
|
80
|
+
transform-origin: var(--origin);
|
|
81
|
+
transition: opacity 140ms ease, transform 140ms ease, visibility 0s linear 140ms;
|
|
82
|
+
}
|
|
83
|
+
.panel[hidden] { display: block; visibility: hidden; opacity: 0; transform: scale(0.96); pointer-events: none; }
|
|
84
|
+
.panel:not([hidden]) { transition-delay: 0s; }
|
|
85
|
+
:host([data-position^="bottom"]) .panel { bottom: 48px; }
|
|
86
|
+
:host([data-position^="top"]) .panel { top: 48px; }
|
|
87
|
+
:host([data-position$="left"]) .panel { left: 0; }
|
|
88
|
+
:host([data-position$="right"]) .panel { right: 0; }
|
|
89
|
+
header {
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
gap: 8px;
|
|
93
|
+
padding: 10px 8px 10px 14px;
|
|
94
|
+
border-bottom: 1px solid var(--border);
|
|
95
|
+
}
|
|
96
|
+
h2 { flex: 1; margin: 0; font-size: 13px; font-weight: 650; }
|
|
97
|
+
.icon-button {
|
|
98
|
+
display: grid;
|
|
99
|
+
place-items: center;
|
|
100
|
+
width: 32px;
|
|
101
|
+
height: 32px;
|
|
102
|
+
padding: 0;
|
|
103
|
+
border: 0;
|
|
104
|
+
border-radius: 8px;
|
|
105
|
+
background: none;
|
|
106
|
+
color: var(--muted);
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
}
|
|
109
|
+
.icon-button:hover { background: var(--surface); color: var(--fg); }
|
|
110
|
+
.icon-button svg { width: 16px; height: 16px; }
|
|
111
|
+
.body { display: flex; flex-direction: column; gap: 12px; padding: 12px 14px 14px; }
|
|
112
|
+
fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
|
|
113
|
+
legend, .label { display: block; margin-bottom: 6px; padding: 0; color: var(--muted); font-size: 11px; font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase; }
|
|
114
|
+
.segmented {
|
|
115
|
+
display: flex;
|
|
116
|
+
gap: 2px;
|
|
117
|
+
padding: 2px;
|
|
118
|
+
border-radius: 9px;
|
|
119
|
+
background: var(--surface);
|
|
120
|
+
}
|
|
121
|
+
.segmented label { flex: 1; position: relative; }
|
|
122
|
+
.segmented input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
|
|
123
|
+
.segmented span {
|
|
124
|
+
display: block;
|
|
125
|
+
padding: 5px 4px;
|
|
126
|
+
border-radius: 7px;
|
|
127
|
+
color: var(--muted);
|
|
128
|
+
font-weight: 550;
|
|
129
|
+
text-align: center;
|
|
130
|
+
text-transform: capitalize;
|
|
131
|
+
white-space: nowrap;
|
|
132
|
+
transition: background-color 120ms ease, color 120ms ease;
|
|
133
|
+
}
|
|
134
|
+
.segmented input:checked + span { background: var(--bg); color: var(--fg); box-shadow: 0 1px 2px rgb(0 0 0 / 0.12); }
|
|
135
|
+
.segmented input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
136
|
+
.select { position: relative; }
|
|
137
|
+
.select select {
|
|
138
|
+
appearance: none;
|
|
139
|
+
width: 100%;
|
|
140
|
+
min-height: 32px;
|
|
141
|
+
padding: 5px 30px 5px 10px;
|
|
142
|
+
border: 1px solid var(--border);
|
|
143
|
+
border-radius: 8px;
|
|
144
|
+
background: var(--surface);
|
|
145
|
+
text-transform: capitalize;
|
|
146
|
+
cursor: pointer;
|
|
147
|
+
}
|
|
148
|
+
.select svg { position: absolute; right: 10px; top: 50%; width: 14px; height: 14px; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
|
|
149
|
+
footer {
|
|
150
|
+
display: flex;
|
|
151
|
+
align-items: center;
|
|
152
|
+
gap: 8px;
|
|
153
|
+
padding: 10px 14px;
|
|
154
|
+
border-top: 1px solid var(--border);
|
|
155
|
+
background: var(--surface);
|
|
156
|
+
}
|
|
157
|
+
.events { flex: 1; color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
|
|
158
|
+
.button {
|
|
159
|
+
min-height: 32px;
|
|
160
|
+
padding: 0 12px;
|
|
161
|
+
border: 0;
|
|
162
|
+
border-radius: 8px;
|
|
163
|
+
background: var(--accent);
|
|
164
|
+
color: var(--on-accent);
|
|
165
|
+
font-weight: 600;
|
|
166
|
+
cursor: pointer;
|
|
167
|
+
}
|
|
168
|
+
.button:hover { filter: brightness(1.08); }
|
|
169
|
+
@media (prefers-reduced-motion: reduce) {
|
|
170
|
+
.launcher, .panel, .segmented span { transition: none; }
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
173
|
+
const SVG = "http://www.w3.org/2000/svg";
|
|
174
|
+
function svgIcon(d) {
|
|
175
|
+
const svg = document.createElementNS(SVG, "svg");
|
|
176
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
177
|
+
svg.setAttribute("fill", "none");
|
|
178
|
+
svg.setAttribute("stroke", "currentColor");
|
|
179
|
+
svg.setAttribute("stroke-width", "2");
|
|
180
|
+
svg.setAttribute("stroke-linecap", "round");
|
|
181
|
+
svg.setAttribute("stroke-linejoin", "round");
|
|
182
|
+
svg.setAttribute("aria-hidden", "true");
|
|
183
|
+
const path = document.createElementNS(SVG, "path");
|
|
184
|
+
path.setAttribute("d", d);
|
|
185
|
+
svg.append(path);
|
|
186
|
+
return svg;
|
|
187
|
+
}
|
|
188
|
+
const ICONS = {
|
|
189
|
+
wrench: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76Z",
|
|
190
|
+
close: "M18 6 6 18M6 6l12 12",
|
|
191
|
+
chevron: "m6 9 6 6 6-6",
|
|
192
|
+
};
|
|
193
|
+
let instance = 0;
|
|
194
|
+
function isOptions(value) {
|
|
195
|
+
return (value !== undefined &&
|
|
196
|
+
!(typeof HTMLElement !== "undefined" && value instanceof HTMLElement));
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Mount the development panel: a small launcher in a corner that opens the
|
|
200
|
+
* panel next to it. Returns `null` when the assistant is disabled. The
|
|
201
|
+
* second argument may also be the parent element, as in earlier versions.
|
|
202
|
+
*/
|
|
203
|
+
export function mountDevAssistant(assistant, options = {}) {
|
|
204
|
+
if (!assistant.getSnapshot().enabled)
|
|
205
|
+
return null;
|
|
206
|
+
const settings = isOptions(options)
|
|
207
|
+
? options
|
|
208
|
+
: { parent: options };
|
|
209
|
+
const parent = settings.parent ?? document.body;
|
|
210
|
+
const position = settings.position ?? "bottom-left";
|
|
211
|
+
const x = settings.offset?.x ?? 16;
|
|
212
|
+
const y = settings.offset?.y ?? 16;
|
|
213
|
+
const id = `pmfa-devtools-${++instance}`;
|
|
214
|
+
const host = document.createElement("div");
|
|
215
|
+
host.dataset.polymorfaDevtools = "";
|
|
216
|
+
host.dataset.position = position;
|
|
217
|
+
const [vertical, horizontal] = position.split("-");
|
|
218
|
+
host.style.setProperty(vertical, `${y}px`);
|
|
219
|
+
host.style.setProperty(horizontal, `${x}px`);
|
|
220
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
221
|
+
const style = document.createElement("style");
|
|
222
|
+
style.textContent = STYLES;
|
|
223
|
+
const launcher = document.createElement("button");
|
|
224
|
+
launcher.type = "button";
|
|
225
|
+
launcher.className = "launcher";
|
|
226
|
+
launcher.setAttribute("aria-label", "Polymorfa dev mode");
|
|
227
|
+
launcher.setAttribute("aria-controls", `${id}-panel`);
|
|
228
|
+
launcher.title = "Polymorfa dev mode";
|
|
229
|
+
launcher.append(svgIcon(ICONS.wrench));
|
|
230
|
+
const panel = document.createElement("section");
|
|
231
|
+
panel.className = "panel";
|
|
232
|
+
panel.id = `${id}-panel`;
|
|
233
|
+
panel.setAttribute("role", "dialog");
|
|
234
|
+
panel.setAttribute("aria-label", "Polymorfa developer assistant");
|
|
235
|
+
panel.style.setProperty("--origin", `${vertical} ${horizontal}`);
|
|
236
|
+
const header = document.createElement("header");
|
|
237
|
+
const title = document.createElement("h2");
|
|
238
|
+
title.textContent = "Polymorfa dev mode";
|
|
239
|
+
const close = document.createElement("button");
|
|
240
|
+
close.type = "button";
|
|
241
|
+
close.className = "icon-button";
|
|
242
|
+
close.setAttribute("aria-label", "Close dev mode");
|
|
243
|
+
close.append(svgIcon(ICONS.close));
|
|
244
|
+
header.append(title, close);
|
|
245
|
+
const body = document.createElement("div");
|
|
246
|
+
body.className = "body";
|
|
247
|
+
const controls = [
|
|
248
|
+
segmented(`${id}-theme`, "Theme", ["system", "light", "dark"], (value) => assistant.configure({ theme: value })),
|
|
249
|
+
segmented(`${id}-direction`, "Direction", ["auto", "ltr", "rtl"], (value) => assistant.configure({ direction: value })),
|
|
250
|
+
segmented(`${id}-motion`, "Motion", ["system", "full", "reduced"], (value) => assistant.configure({
|
|
251
|
+
motion: value,
|
|
252
|
+
})),
|
|
253
|
+
select(`${id}-viewport`, "Viewport", ["responsive", "mobile", "tablet", "desktop"], (value) => assistant.configure({
|
|
254
|
+
viewport: value,
|
|
255
|
+
})),
|
|
256
|
+
segmented(`${id}-network`, "Network", ["online", "slow", "offline"], (value) => assistant.configure({
|
|
257
|
+
network: value,
|
|
258
|
+
})),
|
|
259
|
+
];
|
|
260
|
+
body.append(...controls.map((control) => control.node));
|
|
261
|
+
const footer = document.createElement("footer");
|
|
262
|
+
const events = document.createElement("span");
|
|
263
|
+
events.className = "events";
|
|
264
|
+
events.setAttribute("aria-live", "polite");
|
|
265
|
+
const copy = document.createElement("button");
|
|
266
|
+
copy.type = "button";
|
|
267
|
+
copy.className = "button";
|
|
268
|
+
copy.textContent = "Copy configuration";
|
|
269
|
+
let copyTimer;
|
|
270
|
+
copy.addEventListener("click", () => {
|
|
271
|
+
void navigator.clipboard
|
|
272
|
+
?.writeText(assistant.copyableConfig())
|
|
273
|
+
.then(() => {
|
|
274
|
+
copy.textContent = "Copied";
|
|
275
|
+
clearTimeout(copyTimer);
|
|
276
|
+
copyTimer = setTimeout(() => {
|
|
277
|
+
copy.textContent = "Copy configuration";
|
|
278
|
+
}, 1500);
|
|
279
|
+
})
|
|
280
|
+
.catch(() => undefined);
|
|
281
|
+
});
|
|
282
|
+
footer.append(events, copy);
|
|
283
|
+
panel.append(header, body, footer);
|
|
284
|
+
shadow.append(style, panel, launcher);
|
|
285
|
+
parent.append(host);
|
|
286
|
+
let isOpen = readOpen() ?? settings.defaultOpen ?? false;
|
|
287
|
+
const apply = () => {
|
|
288
|
+
panel.hidden = !isOpen;
|
|
289
|
+
launcher.setAttribute("aria-expanded", String(isOpen));
|
|
290
|
+
};
|
|
291
|
+
const setOpen = (next, focus) => {
|
|
292
|
+
if (next === isOpen)
|
|
293
|
+
return;
|
|
294
|
+
isOpen = next;
|
|
295
|
+
writeOpen(next);
|
|
296
|
+
apply();
|
|
297
|
+
if (!focus)
|
|
298
|
+
return;
|
|
299
|
+
if (next)
|
|
300
|
+
panel
|
|
301
|
+
.querySelector("input:checked, select, button")
|
|
302
|
+
?.focus();
|
|
303
|
+
else
|
|
304
|
+
launcher.focus();
|
|
305
|
+
};
|
|
306
|
+
apply();
|
|
307
|
+
// Opening from the launcher moves focus into the panel, which precedes the
|
|
308
|
+
// launcher in tab order; closing leaves focus on the launcher itself.
|
|
309
|
+
launcher.addEventListener("click", () => setOpen(!isOpen, true));
|
|
310
|
+
close.addEventListener("click", () => setOpen(false, true));
|
|
311
|
+
shadow.addEventListener("keydown", (event) => {
|
|
312
|
+
const key = event;
|
|
313
|
+
if (key.key !== "Escape" || !isOpen || key.isComposing)
|
|
314
|
+
return;
|
|
315
|
+
key.preventDefault();
|
|
316
|
+
key.stopPropagation();
|
|
317
|
+
setOpen(false, true);
|
|
318
|
+
});
|
|
319
|
+
const render = () => {
|
|
320
|
+
const snapshot = assistant.getSnapshot();
|
|
321
|
+
panel.dir = snapshot.direction === "rtl" ? "rtl" : "ltr";
|
|
322
|
+
const values = [
|
|
323
|
+
snapshot.appearance.theme,
|
|
324
|
+
snapshot.direction,
|
|
325
|
+
snapshot.motion,
|
|
326
|
+
snapshot.viewport,
|
|
327
|
+
snapshot.network,
|
|
328
|
+
];
|
|
329
|
+
controls.forEach((control, index) => control.set(values[index] ?? ""));
|
|
330
|
+
const count = snapshot.events.length;
|
|
331
|
+
events.textContent = `${count} redacted request event${count === 1 ? "" : "s"}`;
|
|
332
|
+
};
|
|
333
|
+
const unsubscribe = assistant.subscribe(render);
|
|
334
|
+
render();
|
|
335
|
+
return {
|
|
336
|
+
element: host,
|
|
337
|
+
get isOpen() {
|
|
338
|
+
return isOpen;
|
|
339
|
+
},
|
|
340
|
+
open: () => setOpen(true, false),
|
|
341
|
+
close: () => setOpen(false, false),
|
|
342
|
+
toggle: () => setOpen(!isOpen, false),
|
|
343
|
+
dispose: () => {
|
|
344
|
+
unsubscribe();
|
|
345
|
+
clearTimeout(copyTimer);
|
|
346
|
+
host.remove();
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function segmented(name, label, options, onChange) {
|
|
351
|
+
const fieldset = document.createElement("fieldset");
|
|
352
|
+
const legend = document.createElement("legend");
|
|
353
|
+
legend.textContent = label;
|
|
354
|
+
const group = document.createElement("div");
|
|
355
|
+
group.className = "segmented";
|
|
356
|
+
const inputs = options.map((option) => {
|
|
357
|
+
const wrapper = document.createElement("label");
|
|
358
|
+
const input = document.createElement("input");
|
|
359
|
+
input.type = "radio";
|
|
360
|
+
input.name = name;
|
|
361
|
+
input.value = option;
|
|
362
|
+
input.addEventListener("change", () => {
|
|
363
|
+
if (input.checked)
|
|
364
|
+
onChange(option);
|
|
365
|
+
});
|
|
366
|
+
const text = document.createElement("span");
|
|
367
|
+
text.textContent = option;
|
|
368
|
+
wrapper.append(input, text);
|
|
369
|
+
group.append(wrapper);
|
|
370
|
+
return input;
|
|
371
|
+
});
|
|
372
|
+
fieldset.append(legend, group);
|
|
373
|
+
return {
|
|
374
|
+
node: fieldset,
|
|
375
|
+
set: (value) => {
|
|
376
|
+
for (const input of inputs)
|
|
377
|
+
input.checked = input.value === value;
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function select(id, label, options, onChange) {
|
|
382
|
+
const wrapper = document.createElement("div");
|
|
383
|
+
const caption = document.createElement("label");
|
|
384
|
+
caption.className = "label";
|
|
385
|
+
caption.htmlFor = id;
|
|
386
|
+
caption.textContent = label;
|
|
387
|
+
const box = document.createElement("div");
|
|
388
|
+
box.className = "select";
|
|
389
|
+
const control = document.createElement("select");
|
|
390
|
+
control.id = id;
|
|
391
|
+
for (const option of options) {
|
|
392
|
+
const item = document.createElement("option");
|
|
393
|
+
item.value = option;
|
|
394
|
+
item.textContent = option;
|
|
395
|
+
control.add(item);
|
|
396
|
+
}
|
|
397
|
+
control.addEventListener("change", () => onChange(control.value));
|
|
398
|
+
box.append(control, svgIcon(ICONS.chevron));
|
|
399
|
+
wrapper.append(caption, box);
|
|
400
|
+
return {
|
|
401
|
+
node: wrapper,
|
|
402
|
+
set: (value) => {
|
|
403
|
+
if (control.value !== value)
|
|
404
|
+
control.value = value;
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"production.d.ts","sourceRoot":"","sources":["../src/production.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAGnE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@polymorfa/devtools",
|
|
3
|
+
"version": "0.1.0-dev.20260922094209",
|
|
4
|
+
"description": "Production-gated configuration, theming, and diagnostics assistant for Polymorfa UI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/polymorfa/sdks.git",
|
|
10
|
+
"directory": "packages/devtools"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/polymorfa/sdks/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/polymorfa/sdks#readme",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./production": {
|
|
25
|
+
"types": "./dist/production.d.ts",
|
|
26
|
+
"import": "./dist/production.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc -p tsconfig.build.json",
|
|
36
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@polymorfa/ui": "0.1.0-dev.20260922094209"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@polymorfa/browser": "0.1.0-dev.20260922094209"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"provenance": true
|
|
47
|
+
}
|
|
48
|
+
}
|