@pixl-app/sdk 0.1.0 → 1.0.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/CHANGELOG.md +24 -0
- package/README.md +18 -3
- package/dist/api-types/device-apps.d.ts +1 -1
- package/dist/api-types/index.d.ts +3 -0
- package/dist/api-types/modules.d.ts +1 -1
- package/dist/api-types/payloads.d.ts +389 -0
- package/dist/api-types/templates.d.ts +6 -0
- package/dist/api-types/widgets.d.ts +555 -0
- package/dist/bindings.d.ts +22 -0
- package/dist/chat.d.ts +1 -1
- package/dist/client.d.ts +127 -164
- package/dist/errors.d.ts +17 -0
- package/dist/events.d.ts +41 -0
- package/dist/feed.d.ts +21 -29
- package/dist/fonts.d.ts +19 -0
- package/dist/frame.d.ts +2 -0
- package/dist/index-nb1y5rx1.js +1932 -0
- package/dist/index.d.ts +12 -3
- package/dist/index.js +38 -873
- package/dist/live-value.d.ts +18 -0
- package/dist/pixl-ui.js +278 -0
- package/dist/pixl.js +4 -4
- package/dist/spotify.d.ts +22 -0
- package/dist/stream.d.ts +30 -0
- package/dist/streamelements.d.ts +14 -0
- package/dist/tts.d.ts +18 -9
- package/dist/twitch.d.ts +68 -0
- package/dist/ui/checkbox.d.ts +27 -0
- package/dist/ui/define.d.ts +2 -0
- package/dist/ui/dialog.d.ts +15 -0
- package/dist/ui/dropdown-helpers.d.ts +48 -0
- package/dist/ui/dropdown.d.ts +65 -0
- package/dist/ui/field.d.ts +48 -0
- package/dist/ui/index.d.ts +14 -0
- package/dist/ui/index.js +1919 -0
- package/dist/ui/inputs.d.ts +29 -0
- package/dist/ui/list.d.ts +31 -0
- package/dist/ui/runtime.d.ts +18 -0
- package/dist/ui/section.d.ts +13 -0
- package/dist/ui/styles.d.ts +6 -0
- package/dist/widget.d.ts +141 -0
- package/package.json +13 -5
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ListItemField, WidgetField } from "../api-types/index.js";
|
|
2
|
+
import type { PixlWidget } from "../widget.js";
|
|
3
|
+
export type Input = {
|
|
4
|
+
el: HTMLElement;
|
|
5
|
+
/** Neuer Wert von außen (live), ohne den Fokus zu stören */
|
|
6
|
+
update(value: unknown): void;
|
|
7
|
+
};
|
|
8
|
+
export type InputHandlers = {
|
|
9
|
+
/** Neuer Wert */
|
|
10
|
+
set(value: unknown): void;
|
|
11
|
+
/** Zähler: relative Änderung (verträgt sich mit Stream Deck und Widget gleichzeitig) */
|
|
12
|
+
add?(delta: number): void;
|
|
13
|
+
/** Knopf gedrückt */
|
|
14
|
+
press?(): void;
|
|
15
|
+
};
|
|
16
|
+
type Field = Exclude<WidgetField, {
|
|
17
|
+
type: "list";
|
|
18
|
+
}> | ListItemField;
|
|
19
|
+
/** Eingabe passend zum Typ des Felds */
|
|
20
|
+
export declare function createInput(field: Field, value: unknown, handlers: InputHandlers, ctx: {
|
|
21
|
+
label: string;
|
|
22
|
+
widget?: PixlWidget | null;
|
|
23
|
+
}): Input;
|
|
24
|
+
/**
|
|
25
|
+
* Vorschläge für `font`-Felder: beliebte Google Fonts. Das Dashboard schlägt alle vor, hier reicht
|
|
26
|
+
* eine kurze Liste, eigene Namen gehen immer
|
|
27
|
+
*/
|
|
28
|
+
export declare const POPULAR_FONTS: readonly string[];
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ListField, ListItem } from "../api-types/index.js";
|
|
2
|
+
import { WidgetBoundElement } from "./field.js";
|
|
3
|
+
export type ListEditDetail = {
|
|
4
|
+
/** Der Eintrag, null beim Hinzufügen */
|
|
5
|
+
item: ListItem | null;
|
|
6
|
+
index: number | null;
|
|
7
|
+
/** Werte speichern: Eintrag ändern bzw. neu anlegen */
|
|
8
|
+
save(values: Record<string, unknown>): Promise<boolean>;
|
|
9
|
+
};
|
|
10
|
+
export declare class PixlList extends WidgetBoundElement {
|
|
11
|
+
static observedAttributes: string[];
|
|
12
|
+
/** Eigene Darstellung eines Eintrags (Zeile bzw. Kachel) */
|
|
13
|
+
renderItem: ((item: ListItem, index: number) => Node | string) | null;
|
|
14
|
+
private items;
|
|
15
|
+
private dialog;
|
|
16
|
+
attributeChangedCallback(): void;
|
|
17
|
+
get listField(): ListField | null;
|
|
18
|
+
protected onValue(value: unknown): void;
|
|
19
|
+
protected renderPlaceholder(): void;
|
|
20
|
+
protected render(): void;
|
|
21
|
+
/** Name eines Eintrags: itemLabel, sonst der erste Text */
|
|
22
|
+
labelOf(item: ListItem): string;
|
|
23
|
+
private renderItems;
|
|
24
|
+
private renderTiles;
|
|
25
|
+
private attachDrag;
|
|
26
|
+
private iconButton;
|
|
27
|
+
private op;
|
|
28
|
+
/** Eintrag bearbeiten (Index) oder neu anlegen (null) */
|
|
29
|
+
edit(index: number | null): void;
|
|
30
|
+
private openBuiltInEditor;
|
|
31
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Pixl } from "../client.js";
|
|
2
|
+
import type { PixlWidget } from "../widget.js";
|
|
3
|
+
type PixlClass = {
|
|
4
|
+
shared(): Pixl;
|
|
5
|
+
};
|
|
6
|
+
/** @internal */
|
|
7
|
+
export declare function setPixlClass(cls: PixlClass): void;
|
|
8
|
+
/** Gemeinsame Pixl-Instanz der Seite */
|
|
9
|
+
export declare function sharedPixl(): Pixl;
|
|
10
|
+
/** Das Widget der Seite, sobald die Verbindung steht (null mit normalem API-Schlüssel) */
|
|
11
|
+
export declare function sharedWidget(): Promise<PixlWidget | null>;
|
|
12
|
+
/** Dashboard um Vollbild bitten bzw. es wieder freigeben (nur auf Steuer-Seiten wirksam) */
|
|
13
|
+
export declare function requestFullscreen(on: boolean): void;
|
|
14
|
+
/** @internal von pixl-dialog. Solange ein Dialog offen ist, scrollt die Seite dahinter nicht */
|
|
15
|
+
export declare function dialogOpened(): void;
|
|
16
|
+
/** @internal von pixl-dialog */
|
|
17
|
+
export declare function dialogClosed(): void;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class PixlSection extends HTMLElement {
|
|
2
|
+
static observedAttributes: string[];
|
|
3
|
+
private readonly root;
|
|
4
|
+
constructor();
|
|
5
|
+
attributeChangedCallback(): void;
|
|
6
|
+
private sync;
|
|
7
|
+
}
|
|
8
|
+
export declare class PixlControls extends HTMLElement {
|
|
9
|
+
static observedAttributes: string[];
|
|
10
|
+
connectedCallback(): void;
|
|
11
|
+
attributeChangedCallback(): void;
|
|
12
|
+
private build;
|
|
13
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const SHADOW_CSS = "\n\t:host { \n\t--bg: var(--pixl-bg, #0f0f12);\n\t--surface: var(--pixl-surface, #22222c);\n\t--fg: var(--pixl-fg, #e8e8f0);\n\t--muted: var(--pixl-muted, #9a9aa8);\n\t--line: var(--pixl-line, rgb(255 255 255 / 0.1));\n\t--line-strong: var(--pixl-line-strong, rgb(255 255 255 / 0.16));\n\t--accent: var(--pixl-accent, #7c6dfa);\n\t--danger: var(--pixl-danger, #f05252);\n\t--radius: 10px;\n display: block; color: var(--fg); font-family: var(--pixl-font, system-ui, sans-serif); font-size: 14px; }\n\t:host([hidden]) { display: none; }\n\t* { box-sizing: border-box; }\n\t\n\t.btn {\n\t\theight: 34px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--line);\n\t\tbackground: transparent; color: var(--fg); font: inherit; font-size: 14px; cursor: pointer;\n\t\tdisplay: inline-flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap;\n\t}\n\t.btn:hover:not(:disabled) { border-color: color-mix(in oklch, var(--fg) 35%, transparent); }\n\t.btn:disabled { opacity: 0.45; cursor: not-allowed; }\n\t.btn.primary { background: var(--accent); border-color: transparent; color: white; font-weight: 600; }\n\t.btn.danger { background: var(--danger); border-color: transparent; color: white; }\n\t.btn.ghost { border-color: transparent; }\n\t.btn.icon { width: 34px; padding: 0; }\n\t.input {\n\t\theight: 34px; width: 100%; box-sizing: border-box; border-radius: 8px; border: 1px solid var(--line);\n\t\tbackground: rgb(0 0 0 / 0.2); color: var(--fg); padding: 0 10px; font: inherit; font-size: 14px;\n\t}\n\ttextarea.input { height: auto; min-height: 80px; padding: 8px 10px; }\n\t.btn:focus-visible, .input:focus-visible, .switch:focus-visible {\n\t\toutline: 2px solid var(--accent); outline-offset: 1px;\n\t}\n\t@media (max-width: 640px) {\n\t\t.btn { height: 40px; }\n\t\t.btn.icon { width: 40px; }\n\t\t.input { height: 40px; }\n\t}\n\n\t.field { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; padding: 12px 0; }\n\t.field.wide { flex-direction: column; align-items: stretch; }\n\t.label { display: flex; flex-direction: column; gap: 2px; min-width: 0; }\n\t.label-text { font-weight: 500; }\n\t.desc { color: var(--muted); font-size: 12px; }\n\t.control { display: flex; align-items: center; gap: 8px; min-width: 0; }\n\t.field.wide .control { width: 100%; }\n\t.error { color: var(--danger); font-size: 12px; width: 100%; }\n\t.skeleton { display: block; border-radius: 8px; background: var(--surface); animation: pixl-pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; }\n\t@keyframes pixl-pulse { 50% { opacity: .5; } }\n\t@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }\n\t.switch {\n\t\tappearance: none; width: 40px; height: 22px; border-radius: 999px; border: 1px solid var(--line);\n\t\tbackground: var(--surface); position: relative; cursor: pointer; padding: 0; flex: none; transition: background .2s;\n\t}\n\t.switch::after {\n\t\tcontent: \"\"; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;\n\t\tbackground: var(--fg); transition: transform .2s;\n\t}\n\t.switch[aria-checked=\"true\"] { background: var(--accent); border-color: transparent; }\n\t.switch[aria-checked=\"true\"]::after { transform: translateX(18px); }\n\t.count { min-width: 3ch; text-align: center; font-variant-numeric: tabular-nums; font-size: 18px; font-family: ui-monospace, monospace; }\n\tinput[type=\"range\"] { flex: 1; min-width: 120px; accent-color: var(--accent); }\n\t.value { min-width: 3ch; text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }\n\tinput[type=\"color\"] { width: 40px; height: 34px; padding: 2px; border-radius: 8px; border: 1px solid var(--line); background: transparent; cursor: pointer; flex: none; }\n\t.thumb { width: 48px; height: 48px; border-radius: 8px; border: 1px solid var(--line); background: rgb(0 0 0 / 0.25); object-fit: contain; flex: none; }\n\t.thumb.empty { display: grid; place-items: center; color: var(--muted); font-size: 11px; }\n\t.stack { display: flex; flex-direction: column; gap: 8px; min-width: 0; flex: 1; }\n\t.row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }\n\t.field:not(.wide) .control pixl-dropdown { min-width: 160px; }\n\t.secret-wrap { position: relative; width: 100%; }\n\t.secret-wrap .input { padding-right: 40px; }\n\t.secret-wrap .eye { position: absolute; top: 50%; right: 3px; transform: translateY(-50%); width: 28px; height: 28px; color: var(--muted); }\n\t.secret-wrap .eye:hover:not(:disabled) { color: var(--fg); }\n";
|
|
2
|
+
export declare const PAGE_CSS = "\n\t:where(:root) { color-scheme: dark; }\n\t:where(body) { font-family: var(--pixl-font, system-ui, sans-serif); color: var(--pixl-fg, #e8e8f0); }\n\t:where(pixl-field, pixl-toggle, pixl-counter, pixl-range, pixl-select, pixl-text, pixl-color, pixl-file, pixl-button, pixl-list, pixl-section, pixl-controls) { display: block; }\n\t:where(.pixl-btn, .pixl-input, .pixl-select, .pixl-checkbox, .pixl-card, .pixl-skeleton) { \n\t--bg: var(--pixl-bg, #0f0f12);\n\t--surface: var(--pixl-surface, #22222c);\n\t--fg: var(--pixl-fg, #e8e8f0);\n\t--muted: var(--pixl-muted, #9a9aa8);\n\t--line: var(--pixl-line, rgb(255 255 255 / 0.1));\n\t--line-strong: var(--pixl-line-strong, rgb(255 255 255 / 0.16));\n\t--accent: var(--pixl-accent, #7c6dfa);\n\t--danger: var(--pixl-danger, #f05252);\n\t--radius: 10px;\n }\n\t.pixl-btn {\n\t\theight: 34px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--line);\n\t\tbackground: transparent; color: var(--fg); font: inherit; cursor: pointer;\n\t\tdisplay: inline-flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap;\n\t}\n\t.pixl-btn:disabled { opacity: 0.45; cursor: not-allowed; }\n\t.pixl-btn.primary { background: var(--accent); border-color: transparent; color: white; font-weight: 600; }\n\t.pixl-btn.danger { background: var(--danger); border-color: transparent; color: white; }\n\t.pixl-btn.ghost { border-color: transparent; }\n\t.pixl-input {\n\t\theight: 34px; width: 100%; box-sizing: border-box; border-radius: 8px; border: 1px solid var(--line);\n\t\tbackground: rgb(0 0 0 / 0.2); color: var(--fg); padding: 0 10px; font: inherit;\n\t}\n\ttextarea.pixl-input { height: auto; min-height: 80px; padding: 8px 10px; }\n\t/* R\u00FCckfall f\u00FCr ein <select> des Systems: Feld wie <pixl-dropdown>, die Liste bleibt die des Systems */\n\tselect.pixl-select {\n\t\tappearance: none; height: 36px; width: 100%; box-sizing: border-box; border-radius: 10px;\n\t\tborder: 1px solid var(--line-strong); color: var(--fg); padding: 0 32px 0 12px; font: inherit; font-size: 14px;\n\t\tbackground: rgb(0 0 0 / 0.2) no-repeat right 8px center / 16px 16px;\n\t\tbackground-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239a9aa8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\");\n\t\tcursor: pointer; text-overflow: ellipsis;\n\t}\n\tselect.pixl-select:hover:not(:disabled) { border-color: color-mix(in oklch, var(--fg) 30%, transparent); }\n\tselect.pixl-select:focus-visible {\n\t\toutline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 25%, transparent);\n\t}\n\tselect.pixl-select:disabled { opacity: 0.5; cursor: not-allowed; }\n\tselect.pixl-select option { background: var(--surface); color: var(--fg); }\n\t/* H\u00E4kchen f\u00FCr ein normales <input type=\"checkbox\">, sieht aus wie <pixl-checkbox> */\n\tinput[type=\"checkbox\"].pixl-checkbox {\n\t\tappearance: none; width: 16px; height: 16px; margin: 0; flex: none; vertical-align: middle; cursor: pointer;\n\t\tborder-radius: 5px; border: 1px solid color-mix(in oklch, var(--fg) 30%, transparent); background: rgb(0 0 0 / 0.2);\n\t\tbackground-position: center; background-repeat: no-repeat; background-size: 12px 12px;\n\t\ttransition: border-color .15s, box-shadow .15s;\n\t}\n\tinput[type=\"checkbox\"].pixl-checkbox:hover:not(:disabled) { border-color: color-mix(in oklch, var(--fg) 50%, transparent); }\n\tinput[type=\"checkbox\"].pixl-checkbox:checked {\n\t\tborder-color: transparent;\n\t\tbackground-color: var(--accent);\n\t\tbackground-image:\n\t\t\turl(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E\"),\n\t\t\tlinear-gradient(oklch(from var(--accent) calc(l + 0.04) calc(c * 0.92) h), var(--accent));\n\t\tbackground-size: 12px 12px, 100% 100%;\n\t\tbox-shadow: inset 0 1px 0 rgb(255 255 255 / 0.22), 0 1px 2px rgb(0 0 0 / 0.35);\n\t}\n\tinput[type=\"checkbox\"].pixl-checkbox:disabled { opacity: 0.45; cursor: not-allowed; }\n\tinput[type=\"checkbox\"].pixl-checkbox:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }\n\t.pixl-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 16px; }\n\t.pixl-skeleton { display: block; border-radius: 8px; background: var(--surface); animation: pixl-pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; }\n\t@keyframes pixl-pulse { 50% { opacity: .5; } }\n\t@media (prefers-reduced-motion: reduce) { .pixl-skeleton { animation: none; } }\n\t.pixl-btn:focus-visible, .pixl-input:focus-visible { outline: 2px solid var(--pixl-accent, #7c6dfa); outline-offset: 1px; }\n\t@media (max-width: 640px) { .pixl-btn, .pixl-input { height: 40px; } }\n\t@media (pointer: coarse) { select.pixl-select { height: 40px; } }\n";
|
|
3
|
+
/** @internal Styles für ein Shadow DOM, einmal gebaut und geteilt */
|
|
4
|
+
export declare function adoptStyles(root: ShadowRoot, extra?: string): void;
|
|
5
|
+
/** @internal Styles für eigene Elemente der Seite einmal einfügen */
|
|
6
|
+
export declare function injectPageStyles(): void;
|
package/dist/widget.d.ts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { ListItem, StreamWidgetMessage, WidgetChange, WidgetFieldScope, WidgetManifest, WidgetOperation, WidgetState } from "./api-types/index.js";
|
|
2
|
+
import type { Pixl } from "./client.js";
|
|
3
|
+
import type { TtsVoiceInfo } from "./tts.js";
|
|
4
|
+
import { type LiveValue } from "./live-value.js";
|
|
5
|
+
export type { ListItem, WidgetChange, WidgetManifest, WidgetOperation, WidgetState };
|
|
6
|
+
/** Ein Upload des Kanals */
|
|
7
|
+
export type UploadedFile = {
|
|
8
|
+
path: string;
|
|
9
|
+
name: string;
|
|
10
|
+
size: number;
|
|
11
|
+
uploadedAt: string | null;
|
|
12
|
+
};
|
|
13
|
+
/** Stimmen für `voice`-Felder, wie sie `widget.voices()` liefert */
|
|
14
|
+
export type WidgetVoices = {
|
|
15
|
+
/** TTS ist im Kanal an. Sonst bleibt der Wert stehen, vorlesen geht aber nicht */
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
voices: TtsVoiceInfo[];
|
|
18
|
+
/** Stimme aus den TTS-Einstellungen des Kanals, die ein leerer Wert meint */
|
|
19
|
+
default: string;
|
|
20
|
+
};
|
|
21
|
+
/** Änderung an einer Liste, wie sie bei `onList()` ankommt */
|
|
22
|
+
export type ListChange = {
|
|
23
|
+
type: "insert";
|
|
24
|
+
item: ListItem;
|
|
25
|
+
at: number;
|
|
26
|
+
} | {
|
|
27
|
+
type: "update";
|
|
28
|
+
item: ListItem;
|
|
29
|
+
previous: ListItem | null;
|
|
30
|
+
} | {
|
|
31
|
+
type: "remove";
|
|
32
|
+
id: string;
|
|
33
|
+
previous: ListItem | null;
|
|
34
|
+
} | {
|
|
35
|
+
type: "move";
|
|
36
|
+
id: string;
|
|
37
|
+
to: number;
|
|
38
|
+
} | {
|
|
39
|
+
type: "replace";
|
|
40
|
+
items: ListItem[];
|
|
41
|
+
};
|
|
42
|
+
type Op = WidgetOperation & {
|
|
43
|
+
scope?: WidgetFieldScope;
|
|
44
|
+
};
|
|
45
|
+
/** Schreiben in eine Liste */
|
|
46
|
+
export type ListWriter = {
|
|
47
|
+
insert(item: Record<string, unknown>, at?: number): Promise<void>;
|
|
48
|
+
update(id: string, patch: Record<string, unknown>): Promise<void>;
|
|
49
|
+
remove(id: string): Promise<void>;
|
|
50
|
+
move(id: string, to: number): Promise<void>;
|
|
51
|
+
/** Ganze Liste ersetzen (Import, alles löschen) */
|
|
52
|
+
replace(items: Record<string, unknown>[]): Promise<void>;
|
|
53
|
+
};
|
|
54
|
+
export declare class PixlWidget {
|
|
55
|
+
private readonly pixl;
|
|
56
|
+
/** Ganzer Stand: Name, Version, Felder, Werte */
|
|
57
|
+
readonly state: LiveValue<WidgetState | null>;
|
|
58
|
+
private readonly stateValue;
|
|
59
|
+
private readonly fieldValues;
|
|
60
|
+
private readonly actionHandlers;
|
|
61
|
+
private readonly listHandlers;
|
|
62
|
+
/**
|
|
63
|
+
* Alle Felder als einfaches Objekt, immer auf dem aktuellen Stand: Steuerelemente vor
|
|
64
|
+
* Einstellungen, wie die CSS-Variablen. Zum Lesen, ändern geht über `set`
|
|
65
|
+
* @example const { text, color } = widget.fields
|
|
66
|
+
*/
|
|
67
|
+
readonly fields: Readonly<Record<string, unknown>>;
|
|
68
|
+
/** Werte der Steuerelemente als Live-Werte, z.B. `widget.values.deaths.subscribe(…)` */
|
|
69
|
+
readonly values: Record<string, LiveValue<unknown>>;
|
|
70
|
+
/** Einstellungen als Live-Werte, z.B. `widget.settings.accent.value` */
|
|
71
|
+
readonly settings: Record<string, LiveValue<unknown>>;
|
|
72
|
+
/** Uploads des Kanals, nur auf der eigenen Steuer-Seite (z.B. für eine Bild-Auswahl) */
|
|
73
|
+
readonly files: {
|
|
74
|
+
/** Alle Uploads, neueste zuerst */
|
|
75
|
+
list: () => Promise<UploadedFile[]>;
|
|
76
|
+
/** Datei hochladen, der Pfad passt als Wert eines file-Felds */
|
|
77
|
+
upload: (file: Blob, name?: string) => Promise<{
|
|
78
|
+
path: string;
|
|
79
|
+
url: string;
|
|
80
|
+
}>;
|
|
81
|
+
};
|
|
82
|
+
private voicesCache;
|
|
83
|
+
/**
|
|
84
|
+
* Stimmen des Kanals für `voice`-Felder, nur auf der eigenen Steuer-Seite (z.B. für eine eigene
|
|
85
|
+
* Auswahl). Braucht kein `tts:read`, wird eine halbe Minute gemerkt
|
|
86
|
+
*/
|
|
87
|
+
voices(): Promise<WidgetVoices>;
|
|
88
|
+
/**
|
|
89
|
+
* Vorschau im Arbeitsplatz: was das Widget selbst setzt, gilt nur hier (pixl rechnet, speichert
|
|
90
|
+
* aber nichts), und die Felder kommen aus dem Entwurf statt aus der Version in OBS
|
|
91
|
+
*/
|
|
92
|
+
private readonly preview;
|
|
93
|
+
/** CSS-Variablen in der Seite (bindings.ts), nicht auf Steuer-Seiten */
|
|
94
|
+
private readonly bindings;
|
|
95
|
+
constructor(pixl: Pixl, initial: WidgetState | null, options?: {
|
|
96
|
+
preview?: boolean;
|
|
97
|
+
bind?: boolean;
|
|
98
|
+
});
|
|
99
|
+
/** Volle Adresse eines Werts aus einem file-Feld (`/files/…`), externe URLs bleiben */
|
|
100
|
+
fileUrl(path: string): string;
|
|
101
|
+
/** Felder aus pixl.json */
|
|
102
|
+
get manifest(): WidgetManifest | null;
|
|
103
|
+
/** Aktueller Wert eines Steuerelements oder einer Einstellung */
|
|
104
|
+
get<T = unknown>(name: string): T;
|
|
105
|
+
/** Knopf (`button`) gedrückt. Gibt eine Funktion zum Abmelden zurück */
|
|
106
|
+
on(action: string, handler: () => void): () => void;
|
|
107
|
+
/** Jede Änderung an einer Liste einzeln, z.B. um genau den abgehakten Eintrag zu animieren */
|
|
108
|
+
onList(name: string, handler: (change: ListChange) => void): () => void;
|
|
109
|
+
/** Wert setzen. Der Server prüft und klemmt (min, max, step, options) */
|
|
110
|
+
set(name: string, value: unknown): Promise<void>;
|
|
111
|
+
/** Zahl ändern, z.B. `add("deaths", 1)`. Kommt nicht mit anderen in Konflikt */
|
|
112
|
+
add(name: string, delta: number): Promise<void>;
|
|
113
|
+
/** Schalter umschalten */
|
|
114
|
+
toggle(name: string): Promise<void>;
|
|
115
|
+
/** Knopf drücken (auch von einer Steuer-Seite aus) */
|
|
116
|
+
press(name: string): Promise<void>;
|
|
117
|
+
/** In eine Liste schreiben */
|
|
118
|
+
list(name: string): ListWriter;
|
|
119
|
+
/**
|
|
120
|
+
* Mehrere Änderungen auf einmal, alle oder keine (z.B. einen Entwurf veröffentlichen).
|
|
121
|
+
* Einstellungen (`scope: "settings"`) ändern nur Steuer-Seiten mit dem Recht dazu.
|
|
122
|
+
*/
|
|
123
|
+
batch(operations: Op[]): Promise<void>;
|
|
124
|
+
private previewBatch;
|
|
125
|
+
private fromServer;
|
|
126
|
+
/** @internal Stand neu laden (nach `refetch`) */
|
|
127
|
+
_refetch(): Promise<void>;
|
|
128
|
+
/** @internal */
|
|
129
|
+
_setState(state: WidgetState): void;
|
|
130
|
+
/** @internal Nachricht aus dem Live-Stream */
|
|
131
|
+
_handle(message: StreamWidgetMessage): void;
|
|
132
|
+
private applyState;
|
|
133
|
+
private fieldProxy;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Felder eines Stands als einfaches Objekt zum Lesen (`pixl.fields`, `widget.fields`). Liest bei
|
|
137
|
+
* jedem Zugriff den aktuellen Stand, Steuerelemente vor Einstellungen. Ohne Stand leer
|
|
138
|
+
*/
|
|
139
|
+
export declare function fieldsView(state: () => WidgetState | null): Readonly<Record<string, unknown>>;
|
|
140
|
+
/** Änderung auf einen Stand anwenden (Stand wird verändert) */
|
|
141
|
+
export declare function applyChange(state: WidgetState, change: WidgetChange): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixl-app/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "pixl SDK: Events, Chat, Spotify und TTS eines Kanals in eigenen Widgets, etwa StreamElements Custom Widgets oder OBS-Browserquellen",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://docs.pixl.lennox.zip/sdk",
|
|
@@ -27,7 +27,12 @@
|
|
|
27
27
|
"types": "./dist/index.d.ts",
|
|
28
28
|
"default": "./dist/index.js"
|
|
29
29
|
},
|
|
30
|
-
"./
|
|
30
|
+
"./ui": {
|
|
31
|
+
"types": "./dist/ui/index.d.ts",
|
|
32
|
+
"default": "./dist/ui/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./pixl.js": "./dist/pixl.js",
|
|
35
|
+
"./pixl-ui.js": "./dist/pixl-ui.js"
|
|
31
36
|
},
|
|
32
37
|
"files": [
|
|
33
38
|
"dist",
|
|
@@ -37,14 +42,17 @@
|
|
|
37
42
|
"access": "public"
|
|
38
43
|
},
|
|
39
44
|
"scripts": {
|
|
40
|
-
"
|
|
45
|
+
"dev": "bun scripts/dev.ts",
|
|
46
|
+
"build": "bun run build:script && bun run build:ui && bun run build:esm && bun run build:types",
|
|
41
47
|
"build:script": "bun build ./src/global.ts --outfile ./dist/pixl.js --target browser --format iife --minify",
|
|
42
|
-
"build:esm": "bun build ./src/index.ts
|
|
48
|
+
"build:esm": "bun build ./src/index.ts ./src/ui/index.ts --outdir ./dist --target browser --format esm --splitting --entry-naming [dir]/[name].[ext]",
|
|
43
49
|
"build:types": "tsc -p tsconfig.build.json",
|
|
44
50
|
"check": "tsc -p tsconfig.json",
|
|
45
51
|
"format": "prettier --write .",
|
|
46
52
|
"lint": "prettier --check .",
|
|
47
|
-
"check:version": "bun scripts/check-version.ts"
|
|
53
|
+
"check:version": "bun scripts/check-version.ts",
|
|
54
|
+
"test": "bun test",
|
|
55
|
+
"build:ui": "bun build ./src/ui/global.ts --outfile ./dist/pixl-ui.js --target browser --format iife --minify"
|
|
48
56
|
},
|
|
49
57
|
"devDependencies": {
|
|
50
58
|
"@types/obs-studio": "*",
|