@pixl-app/sdk 0.1.1 → 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.
@@ -0,0 +1,18 @@
1
+ export interface LiveValue<T> {
2
+ /** Aktueller Stand */
3
+ readonly value: T;
4
+ /** Läuft sofort mit dem aktuellen Stand, danach bei jeder Änderung. Gibt eine Funktion zum Abmelden zurück. */
5
+ subscribe(fn: (value: T) => void): () => void;
6
+ }
7
+ /** @internal Live-Wert, den das SDK selbst setzt */
8
+ export declare class WritableLiveValue<T> implements LiveValue<T> {
9
+ private current;
10
+ private readonly listeners;
11
+ constructor(initial: T);
12
+ get value(): T;
13
+ subscribe(fn: (value: T) => void): () => void;
14
+ set(value: T): void;
15
+ /** Alle Abonnenten entfernen (close()) */
16
+ clear(): void;
17
+ }
18
+ export declare function safeCall(fn: () => void): void;
@@ -0,0 +1,278 @@
1
+ (()=>{function U(){if(typeof window>"u"||window.parent===window)return;let e=globalThis;if(e.__pixlFrame)return;e.__pixlFrame=!0,window.addEventListener("message",(n)=>{if(n.source!==window.parent||typeof n.data!=="object"||!n.data)return;if(n.data.type!=="pixl:theme"||typeof n.data.vars!=="object")return;let t=document.documentElement;for(let[r,s]of Object.entries(n.data.vars))if(r.startsWith("--pixl-"))t.style.setProperty(r,String(s));if(n.data.scheme)t.style.colorScheme=n.data.scheme});let i=()=>{let n=document.body,t=getComputedStyle(n),r=n.getBoundingClientRect().height+parseFloat(t.marginTop)+parseFloat(t.marginBottom);window.parent.postMessage({type:"pixl:height",height:Math.ceil(r)},"*")},o=()=>{if(typeof ResizeObserver<"u")new ResizeObserver(i).observe(document.body);i()};if(document.body)o();else document.addEventListener("DOMContentLoaded",o,{once:!0});window.addEventListener("load",i)}var L=`
2
+ :where(:root) { color-scheme: dark; }
3
+ :where(body) { font-family: var(--pixl-font, system-ui, sans-serif); color: var(--pixl-fg, #e8e8f0); }
4
+ :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; }
5
+ :where(.pixl-btn, .pixl-input, .pixl-select, .pixl-checkbox, .pixl-card, .pixl-skeleton) { ${`
6
+ --bg: var(--pixl-bg, #0f0f12);
7
+ --surface: var(--pixl-surface, #22222c);
8
+ --fg: var(--pixl-fg, #e8e8f0);
9
+ --muted: var(--pixl-muted, #9a9aa8);
10
+ --line: var(--pixl-line, rgb(255 255 255 / 0.1));
11
+ --line-strong: var(--pixl-line-strong, rgb(255 255 255 / 0.16));
12
+ --accent: var(--pixl-accent, #7c6dfa);
13
+ --danger: var(--pixl-danger, #f05252);
14
+ --radius: 10px;
15
+ `} }
16
+ .pixl-btn {
17
+ height: 34px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--line);
18
+ background: transparent; color: var(--fg); font: inherit; cursor: pointer;
19
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap;
20
+ }
21
+ .pixl-btn:disabled { opacity: 0.45; cursor: not-allowed; }
22
+ .pixl-btn.primary { background: var(--accent); border-color: transparent; color: white; font-weight: 600; }
23
+ .pixl-btn.danger { background: var(--danger); border-color: transparent; color: white; }
24
+ .pixl-btn.ghost { border-color: transparent; }
25
+ .pixl-input {
26
+ height: 34px; width: 100%; box-sizing: border-box; border-radius: 8px; border: 1px solid var(--line);
27
+ background: rgb(0 0 0 / 0.2); color: var(--fg); padding: 0 10px; font: inherit;
28
+ }
29
+ textarea.pixl-input { height: auto; min-height: 80px; padding: 8px 10px; }
30
+ /* Rückfall für ein <select> des Systems: Feld wie <pixl-dropdown>, die Liste bleibt die des Systems */
31
+ select.pixl-select {
32
+ appearance: none; height: 36px; width: 100%; box-sizing: border-box; border-radius: 10px;
33
+ border: 1px solid var(--line-strong); color: var(--fg); padding: 0 32px 0 12px; font: inherit; font-size: 14px;
34
+ background: rgb(0 0 0 / 0.2) no-repeat right 8px center / 16px 16px;
35
+ background-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");
36
+ cursor: pointer; text-overflow: ellipsis;
37
+ }
38
+ select.pixl-select:hover:not(:disabled) { border-color: color-mix(in oklch, var(--fg) 30%, transparent); }
39
+ select.pixl-select:focus-visible {
40
+ outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 25%, transparent);
41
+ }
42
+ select.pixl-select:disabled { opacity: 0.5; cursor: not-allowed; }
43
+ select.pixl-select option { background: var(--surface); color: var(--fg); }
44
+ /* Häkchen für ein normales <input type="checkbox">, sieht aus wie <pixl-checkbox> */
45
+ input[type="checkbox"].pixl-checkbox {
46
+ appearance: none; width: 16px; height: 16px; margin: 0; flex: none; vertical-align: middle; cursor: pointer;
47
+ border-radius: 5px; border: 1px solid color-mix(in oklch, var(--fg) 30%, transparent); background: rgb(0 0 0 / 0.2);
48
+ background-position: center; background-repeat: no-repeat; background-size: 12px 12px;
49
+ transition: border-color .15s, box-shadow .15s;
50
+ }
51
+ input[type="checkbox"].pixl-checkbox:hover:not(:disabled) { border-color: color-mix(in oklch, var(--fg) 50%, transparent); }
52
+ input[type="checkbox"].pixl-checkbox:checked {
53
+ border-color: transparent;
54
+ background-color: var(--accent);
55
+ background-image:
56
+ url("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"),
57
+ linear-gradient(oklch(from var(--accent) calc(l + 0.04) calc(c * 0.92) h), var(--accent));
58
+ background-size: 12px 12px, 100% 100%;
59
+ box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.22), 0 1px 2px rgb(0 0 0 / 0.35);
60
+ }
61
+ input[type="checkbox"].pixl-checkbox:disabled { opacity: 0.45; cursor: not-allowed; }
62
+ input[type="checkbox"].pixl-checkbox:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
63
+ .pixl-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 16px; }
64
+ .pixl-skeleton { display: block; border-radius: 8px; background: var(--surface); animation: pixl-pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; }
65
+ @keyframes pixl-pulse { 50% { opacity: .5; } }
66
+ @media (prefers-reduced-motion: reduce) { .pixl-skeleton { animation: none; } }
67
+ .pixl-btn:focus-visible, .pixl-input:focus-visible { outline: 2px solid var(--pixl-accent, #7c6dfa); outline-offset: 1px; }
68
+ @media (max-width: 640px) { .pixl-btn, .pixl-input { height: 40px; } }
69
+ @media (pointer: coarse) { select.pixl-select { height: 40px; } }
70
+ `,w=null;function m(e,i=""){if(!w)w=new CSSStyleSheet,w.replaceSync(`
71
+ :host {
72
+ --bg: var(--pixl-bg, #0f0f12);
73
+ --surface: var(--pixl-surface, #22222c);
74
+ --fg: var(--pixl-fg, #e8e8f0);
75
+ --muted: var(--pixl-muted, #9a9aa8);
76
+ --line: var(--pixl-line, rgb(255 255 255 / 0.1));
77
+ --line-strong: var(--pixl-line-strong, rgb(255 255 255 / 0.16));
78
+ --accent: var(--pixl-accent, #7c6dfa);
79
+ --danger: var(--pixl-danger, #f05252);
80
+ --radius: 10px;
81
+ display: block; color: var(--fg); font-family: var(--pixl-font, system-ui, sans-serif); font-size: 14px; }
82
+ :host([hidden]) { display: none; }
83
+ * { box-sizing: border-box; }
84
+
85
+ .btn {
86
+ height: 34px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--line);
87
+ background: transparent; color: var(--fg); font: inherit; font-size: 14px; cursor: pointer;
88
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap;
89
+ }
90
+ .btn:hover:not(:disabled) { border-color: color-mix(in oklch, var(--fg) 35%, transparent); }
91
+ .btn:disabled { opacity: 0.45; cursor: not-allowed; }
92
+ .btn.primary { background: var(--accent); border-color: transparent; color: white; font-weight: 600; }
93
+ .btn.danger { background: var(--danger); border-color: transparent; color: white; }
94
+ .btn.ghost { border-color: transparent; }
95
+ .btn.icon { width: 34px; padding: 0; }
96
+ .input {
97
+ height: 34px; width: 100%; box-sizing: border-box; border-radius: 8px; border: 1px solid var(--line);
98
+ background: rgb(0 0 0 / 0.2); color: var(--fg); padding: 0 10px; font: inherit; font-size: 14px;
99
+ }
100
+ textarea.input { height: auto; min-height: 80px; padding: 8px 10px; }
101
+ .btn:focus-visible, .input:focus-visible, .switch:focus-visible {
102
+ outline: 2px solid var(--accent); outline-offset: 1px;
103
+ }
104
+ @media (max-width: 640px) {
105
+ .btn { height: 40px; }
106
+ .btn.icon { width: 40px; }
107
+ .input { height: 40px; }
108
+ }
109
+
110
+ .field { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; padding: 12px 0; }
111
+ .field.wide { flex-direction: column; align-items: stretch; }
112
+ .label { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
113
+ .label-text { font-weight: 500; }
114
+ .desc { color: var(--muted); font-size: 12px; }
115
+ .control { display: flex; align-items: center; gap: 8px; min-width: 0; }
116
+ .field.wide .control { width: 100%; }
117
+ .error { color: var(--danger); font-size: 12px; width: 100%; }
118
+ .skeleton { display: block; border-radius: 8px; background: var(--surface); animation: pixl-pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; }
119
+ @keyframes pixl-pulse { 50% { opacity: .5; } }
120
+ @media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }
121
+ .switch {
122
+ appearance: none; width: 40px; height: 22px; border-radius: 999px; border: 1px solid var(--line);
123
+ background: var(--surface); position: relative; cursor: pointer; padding: 0; flex: none; transition: background .2s;
124
+ }
125
+ .switch::after {
126
+ content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;
127
+ background: var(--fg); transition: transform .2s;
128
+ }
129
+ .switch[aria-checked="true"] { background: var(--accent); border-color: transparent; }
130
+ .switch[aria-checked="true"]::after { transform: translateX(18px); }
131
+ .count { min-width: 3ch; text-align: center; font-variant-numeric: tabular-nums; font-size: 18px; font-family: ui-monospace, monospace; }
132
+ input[type="range"] { flex: 1; min-width: 120px; accent-color: var(--accent); }
133
+ .value { min-width: 3ch; text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }
134
+ input[type="color"] { width: 40px; height: 34px; padding: 2px; border-radius: 8px; border: 1px solid var(--line); background: transparent; cursor: pointer; flex: none; }
135
+ .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; }
136
+ .thumb.empty { display: grid; place-items: center; color: var(--muted); font-size: 11px; }
137
+ .stack { display: flex; flex-direction: column; gap: 8px; min-width: 0; flex: 1; }
138
+ .row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
139
+ .field:not(.wide) .control pixl-dropdown { min-width: 160px; }
140
+ .secret-wrap { position: relative; width: 100%; }
141
+ .secret-wrap .input { padding-right: 40px; }
142
+ .secret-wrap .eye { position: absolute; top: 50%; right: 3px; transform: translateY(-50%); width: 28px; height: 28px; color: var(--muted); }
143
+ .secret-wrap .eye:hover:not(:disabled) { color: var(--fg); }
144
+ `);let o=[w];if(i){let n=new CSSStyleSheet;n.replaceSync(i),o.push(n)}e.adoptedStyleSheets=o}function V(){if(typeof document>"u"||document.getElementById("pixl-ui-styles"))return;let e=document.createElement("style");e.id="pixl-ui-styles",e.textContent=L,document.head.prepend(e)}var pe='<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>',ue=`
145
+ :host { display: inline-flex; vertical-align: middle; }
146
+ .control {
147
+ display: inline-flex; align-items: center; gap: 8px; cursor: pointer; outline: none;
148
+ border-radius: 6px; -webkit-tap-highlight-color: transparent;
149
+ }
150
+ .box {
151
+ display: inline-flex; align-items: center; justify-content: center; flex: none;
152
+ width: 16px; height: 16px; border-radius: 5px; border: 1px solid color-mix(in oklch, var(--fg) 30%, transparent);
153
+ background: rgb(0 0 0 / 0.2); color: white; transition: border-color .15s, background .15s, box-shadow .15s;
154
+ }
155
+ .box svg { visibility: hidden; }
156
+ .control:hover .box { border-color: color-mix(in oklch, var(--fg) 50%, transparent); }
157
+ .control[aria-checked="true"] .box {
158
+ border-color: transparent;
159
+ background: var(--accent);
160
+ background-image: linear-gradient(oklch(from var(--accent) calc(l + 0.04) calc(c * 0.92) h), var(--accent));
161
+ box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.22), 0 1px 2px rgb(0 0 0 / 0.35);
162
+ }
163
+ .control[aria-checked="true"] .box svg { visibility: visible; }
164
+ .control:focus-visible .box { outline: 2px solid var(--accent); outline-offset: 2px; }
165
+ .control[aria-disabled="true"] { cursor: not-allowed; opacity: 0.45; }
166
+ .text { display: inline; min-width: 0; }
167
+ .text.empty { display: none; }
168
+ `;class O extends HTMLElement{static formAssociated=!0;static observedAttributes=["checked","disabled","aria-label"];root;control;internals;formDisabled=!1;defaultChecked=null;toggledThisTask=!1;constructor(){super();this.internals=typeof this.attachInternals==="function"?this.attachInternals():null,this.root=this.attachShadow({mode:"open",delegatesFocus:!0}),m(this.root,ue),this.root.innerHTML=`<span class="control" part="control" role="checkbox" tabindex="0"><span class="box" part="box">${pe}</span><span class="text empty" part="label"><slot></slot></span></span>`,this.control=this.root.querySelector(".control"),this.root.querySelector("slot").addEventListener("slotchange",()=>this.syncText()),this.addEventListener("click",(e)=>{let i=e.composedPath()[0];if(i instanceof Element&&i!==this&&i.closest("a, button, input, select, textarea"))return;this.toggleByUser()}),this.control.addEventListener("keydown",(e)=>{if(e.key!==" ")return;if(e.preventDefault(),!e.repeat)this.toggleByUser()})}connectedCallback(){if(Object.prototype.hasOwnProperty.call(this,"checked")){let e=this.checked;delete this.checked,this.checked=e===!0}this.defaultChecked??=this.hasAttribute("checked"),this.sync(),this.syncText()}syncText(){let e=this.root.querySelector("slot");if(!e)return;let i=e.assignedNodes().some((o)=>o.nodeType===Node.ELEMENT_NODE||o.textContent?.trim());this.root.querySelector(".text").classList.toggle("empty",!i)}attributeChangedCallback(){this.sync()}get checked(){return this.hasAttribute("checked")}set checked(e){this.toggleAttribute("checked",Boolean(e))}get disabled(){return this.hasAttribute("disabled")}set disabled(e){this.toggleAttribute("disabled",Boolean(e))}get name(){return this.getAttribute("name")??""}get value(){return this.getAttribute("value")??"on"}get form(){return this.internals?.form??null}formResetCallback(){this.checked=this.defaultChecked===!0}formDisabledCallback(e){this.formDisabled=e,this.sync()}formStateRestoreCallback(e){this.checked=e!==null}get isDisabled(){return this.disabled||this.formDisabled}toggleByUser(){if(this.isDisabled||this.toggledThisTask)return;this.toggledThisTask=!0,setTimeout(()=>this.toggledThisTask=!1),this.checked=!this.checked,this.dispatchEvent(new Event("input",{bubbles:!0,composed:!0})),this.dispatchEvent(new Event("change",{bubbles:!0,composed:!0}))}sync(){if(!this.control)return;let e=this.isDisabled;if(this.control.setAttribute("aria-checked",String(this.checked)),e)this.control.setAttribute("aria-disabled","true");else this.control.removeAttribute("aria-disabled");this.control.tabIndex=e?-1:0;let i=this.textContent?.trim()?"":[...this.internals?.labels??[]].map((n)=>n.textContent?.trim()??"").filter(Boolean).join(" "),o=this.getAttribute("aria-label")??i;if(o)this.control.setAttribute("aria-label",o);else this.control.removeAttribute("aria-label");this.internals?.setFormValue(this.checked?this.value:null)}}var T=null;function Y(e){T=e}function M(){if(!T)throw Error("[pixl] pixl-ui braucht das SDK: pixl.js vor pixl-ui.js einbinden");return T.shared()}var X=null;function x(){return X??=(async()=>{let e=M(),i=e.widget();if(i)return i;return await e.ready.catch(()=>{}),e.widget()})(),X}var A=0;function k(e){if(typeof window>"u"||window.parent===window)return;window.parent.postMessage({type:"pixl:fullscreen",on:e},"*")}var J="";function Q(){if(A++>0)return;J=document.documentElement.style.overflow,document.documentElement.style.overflow="hidden",k(!0)}function Z(){if(A===0||--A>0)return;document.documentElement.style.overflow=J,k(!1)}var he=`
169
+ dialog {
170
+ border: 1px solid var(--line); border-radius: 14px; background: var(--surface); color: var(--fg);
171
+ padding: 0; width: min(560px, calc(100vw - 16px)); max-height: calc(100dvh - 16px); overflow: hidden;
172
+ }
173
+ :host([size="large"]) dialog { width: min(960px, calc(100vw - 16px)); }
174
+ dialog[open] { display: flex; flex-direction: column; }
175
+ dialog::backdrop { background: rgb(0 0 0 / 0.6); }
176
+ header { padding: 18px 20px 6px; }
177
+ h2 { margin: 0 0 4px; font-size: 17px; }
178
+ header p { margin: 0; color: var(--muted); }
179
+ header p:empty { display: none; }
180
+ .body { padding: 12px 20px 16px; overflow: auto; flex: 1; min-height: 0; }
181
+ footer { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 8px; padding: 12px 20px; border-top: 1px solid var(--line); background: rgb(0 0 0 / 0.15); }
182
+ footer.empty { display: none; }
183
+ @media (max-width: 640px) {
184
+ dialog { width: 100%; max-width: 100%; height: fit-content; max-height: 100%; margin: auto 0 0; border-radius: 14px 14px 0 0; }
185
+ :host([size="large"]) dialog { width: 100%; height: 100%; margin: 0; border: 0; border-radius: 0; }
186
+ footer { padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
187
+ }
188
+ `;class P extends HTMLElement{static observedAttributes=["heading","description"];root;dialog;isOpen=!1;constructor(){super();this.root=this.attachShadow({mode:"open"}),m(this.root,he),this.root.innerHTML=`
189
+ <dialog part="dialog">
190
+ <header><h2 part="heading"></h2><p part="description"></p></header>
191
+ <div class="body" part="body"><slot></slot></div>
192
+ <footer part="actions"><slot name="actions"></slot></footer>
193
+ </dialog>`,this.dialog=this.root.querySelector("dialog"),this.dialog.addEventListener("close",()=>this.closed());let e=this.root.querySelector('slot[name="actions"]'),i=this.root.querySelector("footer"),o=()=>i.classList.toggle("empty",e.assignedElements().length===0);e.addEventListener("slotchange",o),o(),this.sync()}attributeChangedCallback(){this.sync()}sync(){this.root.querySelector("h2").textContent=this.getAttribute("heading")??"",this.root.querySelector("header p").textContent=this.getAttribute("description")??""}get opened(){return this.isOpen}open(){if(this.isOpen)return;this.isOpen=!0,Q(),this.dialog.showModal(),this.dispatchEvent(new Event("open"))}close(){if(this.isOpen)this.dialog.close()}closed(){if(!this.isOpen)return;this.isOpen=!1,Z(),this.dispatchEvent(new Event("close"))}disconnectedCallback(){this.close()}}var N=32,ee=10;function te(e){if(!Array.isArray(e))return[];let i=[];for(let o of e)if(typeof o==="string"||typeof o==="number")i.push({value:String(o),label:String(o)});else if(o&&typeof o==="object"&&"value"in o){let{value:n,label:t,disabled:r}=o;i.push({value:String(n),label:t===void 0||t===null?String(n):String(t),disabled:r===!0})}return i}function E(e,i,o){for(let n=i+o;n>=0&&n<e.length;n+=o)if(!e[n].disabled)return n;return-1}function S(e){return E(e,-1,1)}function ie(e){return E(e,e.length,-1)}function ne(e,i,o){if(!i||!e.length)return-1;let n=i.toLocaleLowerCase("de"),t=[...n].every((c)=>c===n[0]),r=t?n[0]:n,s=e.length,a=t?o+1:Math.max(o,0);for(let c=0;c<s;c++){let p=((a+c)%s+s)%s,l=e[p];if(!l.disabled&&l.label.toLocaleLowerCase("de").startsWith(r))return p}return-1}function oe(e,i,o,n=320){let t=o.height-e.bottom-6-8,r=e.top-6-8,s=i>t&&r>t,a=Math.max(s?r:t,42),c=Math.min(i,n,a),p=Math.min(Math.max(e.width,160),Math.max(o.width-16,0)),l=Math.min(Math.max(e.left,8),Math.max(o.width-8-p,8));return{top:s?e.top-6-c:e.bottom+6,left:l,width:p,maxHeight:Math.min(n,a),above:s}}var ge='<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg>',me='<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>',be=`
194
+ :host { display: inline-block; vertical-align: middle; width: 100%; min-width: 0; }
195
+ .trigger {
196
+ display: flex; align-items: center; gap: 8px; width: 100%; min-width: 0; height: 36px;
197
+ padding: 0 8px 0 12px; border-radius: 10px; border: 1px solid var(--line-strong);
198
+ background: rgb(0 0 0 / 0.2); color: var(--fg); font: inherit; font-size: 14px; text-align: left;
199
+ cursor: pointer; outline: none; transition: border-color .15s, box-shadow .15s;
200
+ }
201
+ .trigger:hover:not(:disabled) { border-color: color-mix(in oklch, var(--fg) 30%, transparent); }
202
+ .trigger:focus-visible {
203
+ border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 25%, transparent);
204
+ }
205
+ .trigger[aria-expanded="true"] { border-color: var(--accent); }
206
+ .trigger:disabled { cursor: not-allowed; opacity: 0.5; }
207
+ .text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
208
+ .text.empty { color: color-mix(in oklch, var(--muted) 70%, transparent); }
209
+ .chevron { display: flex; flex: none; color: var(--muted); }
210
+ .list {
211
+ position: fixed; inset: auto; margin: 0; z-index: 2147483000; box-sizing: border-box;
212
+ overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain;
213
+ padding: 4px; border: 1px solid var(--line); border-radius: 10px;
214
+ background: var(--surface); color: var(--fg); font: inherit; font-size: 14px;
215
+ box-shadow: 0 16px 40px -12px rgb(0 0 0 / 0.7); outline: none;
216
+ scrollbar-width: thin; scrollbar-color: rgb(255 255 255 / 0.15) transparent;
217
+ }
218
+ .list.fallback:not(.open) { display: none; }
219
+ .list:popover-open, .list.fallback.open { animation: pixl-pop-in 140ms cubic-bezier(0.2, 0.9, 0.3, 1.2) both; }
220
+ .list.above { --pop-y: -4px; }
221
+ @keyframes pixl-pop-in { from { opacity: 0; transform: translateY(var(--pop-y, 4px)) scale(0.98); } }
222
+ @media (prefers-reduced-motion: reduce) { .list:popover-open, .list.fallback.open { animation: none; } }
223
+ .option {
224
+ display: flex; align-items: center; gap: 8px; height: ${N}px; padding: 0 8px;
225
+ border-radius: 6px; cursor: default; user-select: none;
226
+ }
227
+ .option[data-highlighted] { background: rgb(255 255 255 / 0.06); }
228
+ .option[aria-disabled="true"] { opacity: 0.45; }
229
+ .option .label { display: block; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
230
+ .option .check { display: flex; flex: none; color: var(--accent); }
231
+ .option:not([aria-selected="true"]) .check { visibility: hidden; }
232
+ .none { padding: 6px 8px; color: var(--muted); }
233
+ @media (pointer: coarse) { .trigger { height: 40px; } }
234
+ `,f=typeof HTMLElement<"u"&&"showPopover"in HTMLElement.prototype,fe=0;class I extends HTMLElement{static formAssociated=!0;static observedAttributes=["value","placeholder","disabled","aria-label"];root;trigger;text;list;internals;uid=`pixl-dropdown-${++fe}`;propOptions=null;childOptions=[];observer=null;highlighted=-1;query="";queryTimer;formDisabled=!1;defaultValue=null;listening=!1;frame=0;lastRect="";constructor(){super();if(this.internals=typeof this.attachInternals==="function"?this.attachInternals():null,this.root=this.attachShadow({mode:"open",delegatesFocus:!0}),m(this.root,be),this.root.innerHTML=`
235
+ <button type="button" class="trigger" part="trigger" role="combobox" aria-haspopup="listbox" aria-expanded="false">
236
+ <span class="text"></span><span class="chevron">${ge}</span>
237
+ </button>
238
+ <div class="list" part="list" role="listbox" tabindex="-1"></div>`,this.trigger=this.root.querySelector("button"),this.text=this.root.querySelector(".text"),this.list=this.root.querySelector(".list"),this.list.id=`${this.uid}-list`,this.trigger.setAttribute("aria-controls",this.list.id),f)this.list.setAttribute("popover","auto"),this.trigger.popoverTargetElement=this.list,this.list.addEventListener("beforetoggle",(e)=>{if(e.newState==="open")this.beforeOpen()}),this.list.addEventListener("toggle",(e)=>{if(e.newState==="open")this.afterOpen();else this.afterClose()});else this.list.classList.add("fallback"),this.trigger.addEventListener("click",()=>this.isOpen?this.close():this.open());this.trigger.addEventListener("keydown",(e)=>this.onKeydown(e)),this.trigger.addEventListener("keyup",(e)=>e.key===" "&&e.preventDefault()),this.list.addEventListener("mousedown",(e)=>e.preventDefault()),this.list.addEventListener("click",(e)=>{let i=e.target.closest(".option");if(i)this.pick(Number(i.dataset.index))}),this.list.addEventListener("mousemove",(e)=>{let i=e.target.closest(".option");if(i&&i.getAttribute("aria-disabled")!=="true")this.highlight(Number(i.dataset.index),!1)}),this.addEventListener("click",(e)=>{if(e.composedPath()[0]===this)this.trigger.focus()})}connectedCallback(){for(let e of["options","value"])if(Object.prototype.hasOwnProperty.call(this,e)){let i=this[e];delete this[e],this[e]=i}this.defaultValue??=this.getAttribute("value"),this.readChildren(),this.observer??=new MutationObserver(()=>this.readChildren()),this.observer.observe(this,{childList:!0,subtree:!0,characterData:!0,attributes:!0,attributeFilter:["value","label","disabled","selected"]}),this.render()}disconnectedCallback(){if(this.observer?.disconnect(),f){if(this.list.matches(":popover-open"))this.list.hidePopover()}else this.list.classList.remove("open");if(this.listening)this.afterClose()}attributeChangedCallback(){this.render()}get options(){return this.propOptions??this.childOptions}set options(e){this.propOptions=e===null||e===void 0?null:te(e),this.render()}get value(){let e=this.getAttribute("value");if(e!==null)return e;if(this.hasAttribute("placeholder"))return"";let i=this.propOptions?null:this.querySelector("option[selected]");if(i)return i.value;let o=S(this.options);return o>=0?this.options[o].value:""}set value(e){this.setAttribute("value",e===null||e===void 0?"":String(e))}get disabled(){return this.hasAttribute("disabled")}set disabled(e){this.toggleAttribute("disabled",Boolean(e))}get name(){return this.getAttribute("name")??""}get selectedLabel(){return this.options.find((e)=>e.value===this.value)?.label??""}get form(){return this.internals?.form??null}open(){if(this.isOpen||this.trigger.disabled||!this.isConnected)return;if(f)this.list.showPopover();else this.beforeOpen(),this.list.classList.add("open"),this.afterOpen()}close(){if(!this.isOpen)return;if(f)this.list.hidePopover();else this.list.classList.remove("open"),this.afterClose()}get isOpen(){return f?this.list.matches(":popover-open"):this.list.classList.contains("open")}formResetCallback(){if(this.defaultValue===null)this.removeAttribute("value");else this.setAttribute("value",this.defaultValue)}formDisabledCallback(e){this.formDisabled=e,this.render()}formStateRestoreCallback(e){if(typeof e==="string")this.value=e}readChildren(){let e=[...this.querySelectorAll("option")].map((i)=>({value:i.value,label:i.label||i.textContent?.trim()||i.value,disabled:i.disabled}));if(JSON.stringify(e)===JSON.stringify(this.childOptions))return;this.childOptions=e,this.render()}render(){if(!this.trigger)return;let e=this.options,i=this.value,o=e.find((t)=>t.value===i);this.text.textContent=o?.label??this.getAttribute("placeholder")??"Auswählen",this.text.classList.toggle("empty",!o),this.trigger.disabled=this.disabled||this.formDisabled;let n=this.getAttribute("aria-label")??this.labelText();if(n)this.trigger.setAttribute("aria-label",n);else this.trigger.removeAttribute("aria-label");if(this.internals?.setFormValue(o||i?i:null),this.trigger.disabled)this.close();this.renderList()}labelText(){let e=this.internals?.labels;if(!e?.length)return"";return[...e].map((i)=>i.textContent?.trim()??"").filter(Boolean).join(" ")}renderList(){let e=this.options,i=this.value;if(!e.length){this.list.innerHTML='<div class="none">Keine Einträge</div>',this.highlight(-1,!1);return}if(this.list.replaceChildren(...e.map((o,n)=>{let t=document.createElement("div");if(t.className="option",t.id=`${this.uid}-o${n}`,t.dataset.index=String(n),t.setAttribute("role","option"),t.setAttribute("aria-selected",String(o.value===i)),o.disabled)t.setAttribute("aria-disabled","true");let r=document.createElement("span");r.className="label",r.textContent=o.label;let s=document.createElement("span");return s.className="check",s.innerHTML=me,t.append(r,s),t})),this.highlighted>=e.length)this.highlighted=-1;this.highlight(this.highlighted,!1)}beforeOpen(){let e=this.options,i=e.findIndex((o)=>o.value===this.value&&!o.disabled);this.highlighted=i>=0?i:S(e),this.renderList(),this.position()}afterOpen(){if(this.trigger.setAttribute("aria-expanded","true"),this.highlight(this.highlighted,!0),this.listening)return;if(this.listening=!0,addEventListener("resize",this.onViewportChange,{passive:!0}),!f)document.addEventListener("pointerdown",this.onOutsidePointer,!0);this.lastRect=this.rectKey(),this.frame=requestAnimationFrame(this.follow)}afterClose(){if(this.trigger.setAttribute("aria-expanded","false"),this.trigger.removeAttribute("aria-activedescendant"),this.listening=!1,cancelAnimationFrame(this.frame),removeEventListener("resize",this.onViewportChange),!f)document.removeEventListener("pointerdown",this.onOutsidePointer,!0)}onViewportChange=()=>this.position();rectKey(){let e=this.trigger.getBoundingClientRect();return`${e.top},${e.left},${e.width},${e.height}`}follow=()=>{if(!this.listening)return;let e=this.rectKey();if(e!==this.lastRect)this.lastRect=e,this.position();this.frame=requestAnimationFrame(this.follow)};onOutsidePointer=(e)=>{if(!e.composedPath().includes(this))this.close()};position(){let e=this.trigger.getBoundingClientRect(),i=Math.max(this.options.length,1)*N+ee,o=oe(e,i,{width:innerWidth,height:innerHeight}),n=this.list.style;n.top=`${o.top}px`,n.left=`${o.left}px`,n.width=`${o.width}px`,n.maxHeight=`${o.maxHeight}px`,this.list.classList.toggle("above",o.above)}highlight(e,i){this.highlighted=e;for(let n of this.list.querySelectorAll(".option"))n.toggleAttribute("data-highlighted",Number(n.dataset.index)===e);let o=e>=0?this.list.querySelector(`#${this.uid}-o${e}`):null;if(o&&this.isOpen){if(this.trigger.setAttribute("aria-activedescendant",o.id),i)o.scrollIntoView({block:"nearest"})}else this.trigger.removeAttribute("aria-activedescendant")}pick(e){let i=this.options[e];if(!i||i.disabled)return;let o=i.value!==this.value;if(this.value=i.value,this.close(),this.trigger.focus(),!o)return;this.dispatchEvent(new Event("input",{bubbles:!0,composed:!0})),this.dispatchEvent(new Event("change",{bubbles:!0,composed:!0}))}onKeydown(e){if(e.altKey||e.ctrlKey||e.metaKey){if(e.altKey&&e.key==="ArrowUp"&&this.isOpen)e.preventDefault(),this.pick(this.highlighted);return}let i=this.options,o=this.isOpen,n=(t)=>{if(t>=0)this.highlight(t,!0)};switch(e.key){case"ArrowDown":case"ArrowUp":if(e.preventDefault(),!o)return this.open();return n(E(i,this.highlighted,e.key==="ArrowDown"?1:-1));case"Home":case"PageUp":if(!o)return;return e.preventDefault(),n(S(i));case"End":case"PageDown":if(!o)return;return e.preventDefault(),n(ie(i));case"Enter":return e.preventDefault(),o?this.pick(this.highlighted):this.open();case" ":if(e.preventDefault(),this.query)return this.typeahead(" ");return o?this.pick(this.highlighted):this.open();case"Escape":if(!o)return;return e.preventDefault(),e.stopPropagation(),this.close();case"Tab":if(o)this.close();return;default:if(e.key.length===1)e.preventDefault(),this.typeahead(e.key)}}typeahead(e){clearTimeout(this.queryTimer),this.query+=e,this.queryTimer=setTimeout(()=>this.query="",500);let i=this.options,o=this.isOpen?this.highlighted:i.findIndex((t)=>t.value===this.value),n=ne(i,this.query,o);if(n<0)return;if(this.isOpen)this.highlight(n,!0);else if(i[n].value!==this.value)this.value=i[n].value,this.dispatchEvent(new Event("input",{bubbles:!0,composed:!0})),this.dispatchEvent(new Event("change",{bubbles:!0,composed:!0}))}}var xe=[100,200,300,400,500,600,700,800,900].flatMap((e)=>[`${e}`,`${e}i`]).join(",");function ve(e){return`https://fonts.bunny.net/css?family=${encodeURIComponent(e.trim())}:${xe}&display=swap`}function H(e,i="sans-serif"){let o=typeof e==="string"?e.trim():"";if(!o)return i;return`"${o.replace(/["\\]/g,"\\$&")}", ${i}`}var re=new Map;function se(e){let i=typeof e==="string"?e.trim():"";if(!i||typeof document>"u")return Promise.resolve(!1);let o=re.get(i);if(!o)o=new Promise((n)=>{let t=document.createElement("link");t.rel="stylesheet",t.href=ve(i),t.dataset.pixlFont=i,t.onload=()=>{let r=document.fonts;if(!r)return n(!0);r.load(`16px ${H(i)}`).then((s)=>n(s.length>0),()=>n(!1))},t.onerror=()=>n(!1),document.head.append(t)}),re.set(i,o);return o}function u(e,i={},...o){let n=document.createElement(e);for(let[t,r]of Object.entries(i))if(t.startsWith("aria-")||t==="role"||t==="type")n.setAttribute(t,String(r));else n[t]=r;return n.append(...o),n}function v(e,i,o,n){switch(e.type){case"boolean":{let t=u("button",{type:"button",className:"switch",role:"switch","aria-label":n.label}),r=i===!0,s=()=>t.setAttribute("aria-checked",String(r));return t.onclick=()=>{r=!r,s(),o.set(r)},s(),{el:t,update:(a)=>{r=a===!0,s()}}}case"number":{if(e.slider)return Ee(e,i,o,n);let t=e.step??1,r=u("output",{className:"count"},String(i??0)),s=typeof i==="number"?i:0,a=(d)=>{if(o.add)o.add(d);else s+=d,r.textContent=String(s),o.set(s);l()},c=u("button",{type:"button",className:"btn icon","aria-label":`${n.label} verringern`},"−"),p=u("button",{type:"button",className:"btn icon","aria-label":`${n.label} erhöhen`},"+"),l=()=>{c.disabled=e.min!==void 0&&s<=e.min,p.disabled=e.max!==void 0&&s>=e.max};return c.onclick=()=>a(-t),p.onclick=()=>a(t),l(),{el:u("div",{className:"control"},c,r,p),update:(d)=>{s=typeof d==="number"?d:0,r.textContent=String(s),l()}}}case"enum":{let t=document.createElement("pixl-dropdown");return t.setAttribute("aria-label",n.label),t.options=e.options.map((r)=>({value:r,label:r})),t.value=String(i??e.options[0]??""),t.addEventListener("change",()=>o.set(t.value)),{el:t,update:(r)=>t.value=String(r??"")}}case"string":{if(e.sensitive)return Oe(e,i,o,n);let t=e.multiline?u("textarea",{className:"input","aria-label":n.label}):u("input",{className:"input","aria-label":n.label});if(e.maxLength)t.maxLength=e.maxLength;t.value=typeof i==="string"?i:"";let r=t.value,s=()=>{if(t.value===r)return;r=t.value,o.set(t.value)};return t.onblur=s,t.onkeydown=(a)=>{if(a instanceof KeyboardEvent&&a.key==="Enter"&&!e.multiline)a.preventDefault(),s()},{el:t,update:(a)=>{if(document.activeElement===t)return;t.value=typeof a==="string"?a:"",r=t.value}}}case"color":{let t=u("input",{type:"color","aria-label":n.label}),r=u("input",{className:"input","aria-label":`${n.label} als Hex`,maxLength:9});r.style.fontFamily="ui-monospace, monospace";let s=(a)=>{let c=typeof a==="string"?a:"#ffffff";if(t.value=c.slice(0,7),document.activeElement!==r)r.value=c};return s(i),t.onchange=()=>{r.value=t.value,o.set(t.value)},r.onchange=()=>/^#[0-9a-f]{6}([0-9a-f]{2})?$/i.test(r.value)&&o.set(r.value),{el:u("div",{className:"control"},t,r),update:s}}case"file":return Se(e,i,o,n);case"font":return ke(i,o,n);case"voice":return Ae(i,o,n);case"trigger":{let t=u("button",{type:"button",className:"btn"},n.label);return t.onclick=()=>o.press?.(),{el:t,update:()=>{}}}}}var ye="Roboto,Open Sans,Inter,Montserrat,Poppins,Lato,Arimo,Roboto Condensed,Roboto Mono,Oswald,Noto Sans,DM Sans,Raleway,Nunito Sans,Playfair Display,Nunito,Manrope,Roboto Slab,Ubuntu,Plus Jakarta Sans,JetBrains Mono,Kanit,Mulish,Figtree,PT Sans,Rubik,Black Ops One,Archivo Black,Saira,Outfit,Source Sans 3,Jost,Quicksand,Source Code Pro,Inconsolata,Merriweather,Prompt,Share Tech,Fira Sans,Lora,Space Grotesk,Smooch Sans,Work Sans,Archivo,Karla,Bebas Neue,Noto Serif,Heebo,IBM Plex Sans,Press Start 2P".split(","),we=0;function ke(e,i,o){let n=`pixl-fonts-${++we}`,t=u("input",{className:"input","aria-label":o.label,placeholder:"Schrift des Widgets",autocomplete:"off",spellcheck:!1,maxLength:100});t.setAttribute("list",n);let r=u("datalist",{id:n},...ye.map((l)=>u("option",{value:l}))),s="",a="",c=(l)=>{let d=typeof l==="string"?l:"";if(s=d,document.activeElement!==t)t.value=d;if(a===d)return;if(a="",t.style.fontFamily="",!d)return;se(d).then(()=>{if(s!==d)return;a=d,t.style.fontFamily=H(d,"system-ui, sans-serif")})},p=()=>{let l=(t.value.split(",")[0]??"").replace(/\s+/g," ").replace(/[\p{Cc}"'`;{}<>\\]/gu,"").replace(/ {2,}/g," ").trim().slice(0,100).trim();if(t.value=l,l===s)return;c(l),i.set(l)};return t.onblur=p,t.onkeydown=(l)=>{if(l instanceof KeyboardEvent&&l.key==="Enter")l.preventDefault(),p()},c(e),{el:u("div",{className:"control",style:"width:100%"},t,r),update:c}}function Ee(e,i,o,n){let t=e.min??0,r=u("input",{type:"range",min:String(t),max:String(e.max??100),step:String(e.step??1),value:String(i??t),"aria-label":n.label}),s=u("span",{className:"value"},String(i??t));return r.oninput=()=>s.textContent=r.value,r.onchange=()=>o.set(Number(r.value)),{el:u("div",{className:"control"},r,s),update:(a)=>{if(document.activeElement===r)return;r.value=String(a),s.textContent=String(a)}}}function Se(e,i,o,n){let t=n.widget,r=u("img",{className:"thumb",alt:""}),s=u("input",{className:"input",placeholder:"oder https://…","aria-label":`${n.label}, Adresse`}),a=u("input",{type:"file",accept:e.accept??"",hidden:!0}),c=u("button",{type:"button",className:"btn"},"Hochladen"),p=u("button",{type:"button",className:"btn ghost"},"Entfernen"),l=typeof i==="string"?i:"",d=()=>{let h=l&&t?t.fileUrl(l):l;if(r.hidden=!h,h)r.src=h;if(p.hidden=!l,document.activeElement!==s)s.value=l.startsWith("https://")?l:""},g=(h)=>{l=h,d(),o.set(h)};return c.onclick=()=>a.click(),a.onchange=async()=>{let h=a.files?.[0];if(a.value="",!h||!t)return;c.disabled=!0,c.textContent="Lädt hoch …";try{g((await t.files.upload(h)).path)}catch(y){console.error("[pixl] Hochladen fehlgeschlagen:",y),alert("Hochladen fehlgeschlagen")}finally{c.disabled=!1,c.textContent="Hochladen"}},p.onclick=()=>g(""),s.onchange=()=>{let h=s.value.trim();if(!h||h.startsWith("https://"))g(h)},d(),{el:u("div",{className:"control",style:"align-items:flex-start;width:100%"},r,u("div",{className:"stack"},u("div",{className:"row"},c,p,a),s)),update:(h)=>{l=typeof h==="string"?h:"",d()}}}var Ce='<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0"/><circle cx="12" cy="12" r="3"/></svg>',Le='<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.53 13.53 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"/><path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"/><path d="m2 2 20 20"/></svg>';function Oe(e,i,o,n){let t=u("input",{type:"password",className:"input secret","aria-label":n.label,autocomplete:"off",autocapitalize:"off",spellcheck:!1});for(let d of["data-1p-ignore","data-bwignore"])t.setAttribute(d,"");if(t.setAttribute("data-lpignore","true"),t.setAttribute("data-form-type","other"),e.maxLength)t.maxLength=e.maxLength;let r=u("button",{type:"button",className:"btn ghost icon eye"}),s=u("div",{className:"secret-wrap"},t,r),a=!1,c=(d)=>{a=d,t.type=d?"text":"password",r.innerHTML=d?Le:Ce,r.setAttribute("aria-label",d?`${n.label} verbergen`:`${n.label} anzeigen`),r.setAttribute("aria-pressed",String(d))};c(!1),r.onclick=()=>c(!a),s.addEventListener("focusout",(d)=>{if(!s.contains(d.relatedTarget))c(!1)}),t.value=typeof i==="string"?i:"";let p=t.value,l=()=>{let d=t.value.trim();if(t.value=d,d===p)return;p=d,o.set(d)};return t.onblur=l,t.onkeydown=(d)=>{if(d instanceof KeyboardEvent&&d.key==="Enter")d.preventDefault(),l()},{el:s,update:(d)=>{if(document.activeElement===t)return;t.value=typeof d==="string"?d:"",p=t.value}}}var Te={ai:" (KI)",custom:" (eigene)"},ae={standard:0,ai:1,custom:2};function Ae(e,i,o){let n=document.createElement("pixl-dropdown");n.setAttribute("aria-label",o.label),n.setAttribute("disabled","");let t=u("span",{className:"desc",hidden:!0}),r=typeof e==="string"?e:"",s=new Map,a="",c=()=>{let l=[{value:"",label:a?`Stimme des Kanals (${a})`:"Stimme des Kanals"},...[...s].map(([d,g])=>({value:d,label:g}))];if(r&&!s.has(r))l.push({value:r,label:"Unbekannte Stimme"});n.options=l,n.value=r};c(),n.addEventListener("change",()=>{r=n.value,i.set(r)});let p=o.widget;if(!p)t.textContent="Stimmen gibt es nur in gehosteten Widgets";else p.voices().then((l)=>{let d=[...l.voices].sort((g,h)=>(ae[g.tier??"standard"]??3)-(ae[h.tier??"standard"]??3)||g.name.localeCompare(h.name,"de"));if(s=new Map(d.map((g)=>[g.id,`${g.name}${Te[g.tier??""]??""}`])),a=l.voices.find((g)=>g.id===l.default)?.name??"",c(),l.enabled)n.removeAttribute("disabled");else t.textContent="TTS ist für diesen Kanal ausgeschaltet.",t.hidden=!1}).catch(()=>{t.textContent="Stimmen konnten nicht geladen werden.",t.hidden=!1});if(!p)t.hidden=!1;return{el:u("div",{className:"stack"},n,t),update:(l)=>{r=typeof l==="string"?l:"",c()}}}var Me=(e)=>e.type==="string"||e.type==="file"||e.type==="font"||e.type==="voice"||e.type==="number"&&!!e.slider,Pe={"pixl-toggle":[40,22,999],"pixl-counter":[116,34,999],"pixl-button":[120,34,999],"pixl-color":[120,34,8]};class C extends HTMLElement{widget=null;root;unsubscribe=null;connected=!1;constructor(){super();this.root=this.attachShadow({mode:"open"})}get fieldName(){return this.getAttribute("field")??""}get scope(){let e=this.getAttribute("scope");if(e==="settings"||e==="controls")return e;return this.widget?.manifest?.settings?.[this.fieldName]?"settings":"controls"}get definition(){let e=this.widget?.manifest,i=this.fieldName;return(this.scope==="settings"?e?.settings?.[i]:e?.controls?.[i])??null}connectedCallback(){if(this.connected=!0,!this.widget)this.setAttribute("aria-busy","true"),this.renderPlaceholder();x().then((e)=>{if(!this.connected)return;if(this.removeAttribute("aria-busy"),this.widget=e,!e)return this.renderMessage("Nur in gehosteten Widgets verfügbar");let i=this.scope==="settings"?e.settings[this.fieldName]:e.values[this.fieldName];this.render(),this.unsubscribe=i?.subscribe((o)=>this.onValue(o))??null})}disconnectedCallback(){this.connected=!1,this.unsubscribe?.(),this.unsubscribe=null}async write(e){if(!this.widget)return!1;try{return await this.widget.batch([{...e,name:this.fieldName,scope:this.scope}]),this.showError(null),!0}catch(i){return this.showError(i instanceof Error?i.message:"Speichern fehlgeschlagen"),!1}}showError(e){let i=this.root.querySelector(".error");if(!e)return i?.remove();if(!i)i=document.createElement("p"),i.className="error",i.setAttribute("role","alert"),this.root.querySelector(".field, .list")?.append(i);i.textContent=e}renderPlaceholder(){m(this.root);let[e,i,o]=Pe[this.localName]??[96,34,8],n=document.createElement("div");n.className="field";let t=this.getAttribute("label")??this.textContent?.trim();if(this.localName!=="pixl-button"){let s=document.createElement("span");if(t)s.className="label-text",s.textContent=t;else s.className="skeleton",s.style.cssText="height:14px;width:min(160px,45%)",s.setAttribute("aria-hidden","true");n.append(s)}let r=document.createElement("span");r.className="skeleton",r.style.cssText=`width:${e}px;height:${i}px;border-radius:${o}px`,r.setAttribute("aria-hidden","true"),n.append(r),this.root.replaceChildren(n)}renderMessage(e){m(this.root),this.root.innerHTML=`<p class="desc">${e}</p>`}}class b extends C{static observedAttributes=["field","scope","label","description"];input=null;attributeChangedCallback(){if(this.widget)this.render()}render(){m(this.root);let e=this.definition;if(!e)return this.renderMessage(`Feld „${this.fieldName}“ steht nicht in pixl.json`);if(e.type==="list")return this.renderMessage(`Für Listen <pixl-list field="${this.fieldName}"> nehmen`);let i=this.getAttribute("label")??(this.textContent?.trim()||e.label),o=this.getAttribute("description")??e.description??"",n=this.scope==="settings"?this.widget?.settings[this.fieldName]?.value:this.widget?.values[this.fieldName]?.value,t=document.createElement("div");if(t.className=`field${Me(e)?" wide":""}`,this.input=v(e,n,{set:(s)=>void this.write({set:s}),add:(s)=>void this.write({add:s}),press:()=>void this.write({press:!0})},{label:i,widget:this.widget}),e.type!=="trigger"){let s=document.createElement("div");if(s.className="label",s.innerHTML='<span class="label-text"><slot></slot></span>',s.querySelector("slot").textContent=i,o){let a=document.createElement("span");a.className="desc",a.textContent=o,s.append(a)}t.append(s)}let r=document.createElement("div");r.className="control",r.append(this.input.el),t.append(r),this.root.replaceChildren(t)}onValue(e){this.input?.update(e)}}class D extends b{}class F extends b{}class R extends b{}class j extends b{}class W extends b{}class B extends b{}class z extends b{}class q extends b{}var le=`
239
+ .list { display: flex; flex-direction: column; gap: 10px; }
240
+ /* Einträge vertieft in der Karte des Felds, statt eines zweiten Rahmens */
241
+ ul {
242
+ list-style: none; margin: 0; padding: 0; border-radius: var(--radius);
243
+ background: rgb(0 0 0 / 0.2); box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.25);
244
+ }
245
+ li.row-item { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; padding: 8px 12px; }
246
+ li.row-item + li.row-item { border-top: 1px solid var(--line); }
247
+ .name { flex: 1; min-width: 6rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
248
+ .actions { margin-left: auto; display: flex; gap: 2px; }
249
+ .img { width: 36px; height: 36px; border-radius: 6px; border: 1px solid var(--line); object-fit: contain; background: rgb(0 0 0 / 0.25); flex: none; }
250
+ .tiles {
251
+ display: flex; flex-wrap: wrap; gap: 8px; padding: 12px; border-radius: var(--radius); min-height: 72px; align-items: center;
252
+ background: rgb(0 0 0 / 0.2); box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.25);
253
+ }
254
+ .tile { border: 0; padding: 0; background: transparent; color: inherit; font: inherit; cursor: grab; touch-action: pan-y; user-select: none; border-radius: 8px; }
255
+ .tile.dragging { opacity: 0.35; }
256
+ .tile.before { box-shadow: -4px 0 0 var(--accent); }
257
+ .tile.after { box-shadow: 4px 0 0 var(--accent); }
258
+ .empty { color: var(--muted); padding: 16px; text-align: center; border-radius: var(--radius); background: rgb(0 0 0 / 0.15); }
259
+ .tools { display: flex; flex-wrap: wrap; gap: 8px; }
260
+ .form { display: flex; flex-direction: column; gap: 14px; }
261
+ .form label { display: flex; flex-direction: column; gap: 6px; font-weight: 500; }
262
+ `;class _ extends C{static observedAttributes=["field","scope","layout"];renderItem=null;items=[];dialog=null;attributeChangedCallback(){if(this.widget)this.render()}get listField(){let e=this.definition;return e?.type==="list"?e:null}onValue(e){this.items=Array.isArray(e)?e:[],this.renderItems()}renderPlaceholder(){m(this.root,le);let e='<span class="skeleton" style="width:72px;height:72px"></span>',i='<li class="row-item"><span class="skeleton" style="height:14px;width:45%;margin:11px 0"></span></li>';this.root.innerHTML=this.getAttribute("layout")==="tiles"?`<div class="tiles" aria-hidden="true">${e.repeat(3)}</div>`:`<ul aria-hidden="true">${i.repeat(3)}</ul>`}render(){if(m(this.root,le),!this.listField)return this.renderMessage(`„${this.fieldName}“ ist in pixl.json kein list-Feld`);this.root.innerHTML=`
263
+ <div class="list">
264
+ <div class="items"></div>
265
+ <div class="tools">
266
+ <button type="button" class="btn add">+ Hinzufügen</button>
267
+ </div>
268
+ </div>
269
+ <pixl-dialog size="large"></pixl-dialog>`,this.dialog=this.root.querySelector("pixl-dialog"),this.root.querySelector(".add").onclick=()=>this.edit(null),this.renderItems()}labelOf(e){let i=this.listField,o=i?.itemLabel?e[i.itemLabel]:null;if(typeof o==="string"&&o)return o;for(let[n,t]of Object.entries(i?.item??{}))if((t.type==="string"||t.type==="enum")&&typeof e[n]==="string"&&e[n])return e[n];return"Ohne Namen"}renderItems(){let e=this.root.querySelector(".items"),i=this.listField;if(!e||!i)return;let o=this.root.querySelector(".add");if(o)o.disabled=this.items.length>=(i.max??200);if(!this.items.length){e.innerHTML='<p class="empty">Noch keine Einträge</p>';return}if(this.getAttribute("layout")==="tiles")return this.renderTiles(e);let n=document.createElement("ul");this.items.forEach((t,r)=>{let s=document.createElement("li");s.className="row-item";let a=this.renderItem?.(t,r);if(a!==void 0&&a!==null){let p=document.createElement("div");p.className="name",p.append(a),s.append(p)}else{let p=i.itemImage?t[i.itemImage]:null;if(i.itemImage){let d=document.createElement("img");if(d.className="img",d.alt="",typeof p==="string"&&p)d.src=this.widget?.fileUrl(p)??p;s.append(d)}let l=document.createElement("span");l.className="name",l.textContent=this.labelOf(t),s.append(l)}for(let[p,l]of Object.entries(i.item).filter(([,d])=>d.type==="boolean").slice(0,3)){let d=document.createElement("button");d.type="button",d.className="switch",d.setAttribute("role","switch"),d.setAttribute("aria-checked",String(t[p]===!0)),d.setAttribute("aria-label",`${l.label}: ${this.labelOf(t)}`),d.onclick=()=>this.op({update:t.id,field:p,toggle:!0}),s.append(d)}let c=document.createElement("div");c.className="actions",c.append(this.iconButton("↑",`${this.labelOf(t)} nach oben`,r===0,()=>this.op({move:t.id,to:r-1})),this.iconButton("↓",`${this.labelOf(t)} nach unten`,r===this.items.length-1,()=>this.op({move:t.id,to:r+1})),this.iconButton("✎",`${this.labelOf(t)} bearbeiten`,!1,()=>this.edit(r)),this.iconButton("✕",`${this.labelOf(t)} löschen`,!1,()=>this.op({remove:t.id}))),s.append(c),n.append(s)}),e.replaceChildren(n)}renderTiles(e){let i=document.createElement("div");i.className="tiles",this.items.forEach((o,n)=>{let t=document.createElement("button");t.type="button",t.className="tile",t.setAttribute("aria-label",`${this.labelOf(o)} bearbeiten`);let r=this.renderItem?.(o,n);t.append(r??this.labelOf(o)),this.attachDrag(t,n,i),i.append(t)}),e.replaceChildren(i)}attachDrag(e,i,o){let n=null,t=!1,r=null;e.addEventListener("pointerdown",(a)=>{if(a.button!==0)return;n={x:a.clientX,y:a.clientY},t=!1,r=null,e.setPointerCapture(a.pointerId)}),e.addEventListener("pointermove",(a)=>{if(!n)return;if(!t&&Math.hypot(a.clientX-n.x,a.clientY-n.y)<8)return;t=!0,e.classList.add("dragging");let c=[...o.querySelectorAll(".tile")],p=null;if(c.forEach((d,g)=>{d.classList.remove("before","after");let h=d.getBoundingClientRect(),y=Math.hypot(a.clientX-(h.left+h.width/2),a.clientY-(h.top+h.height/2));if(!p||y<p.d)p={i:g,d:y,after:a.clientX>h.left+h.width/2}}),!p)return;let l=p;c[l.i].classList.add(l.after?"after":"before"),r=l.after?l.i+1:l.i});let s=(a)=>{if(!n)return;if(n=null,a)return this.renderItems();if(!t)return this.edit(i);if(r===null)return this.renderItems();let c=Math.max(0,Math.min(r>i?r-1:r,this.items.length-1));if(c===i)return this.renderItems();this.op({move:this.items[i].id,to:c})};e.addEventListener("pointerup",()=>s(!1)),e.addEventListener("pointercancel",()=>s(!0))}iconButton(e,i,o,n){let t=document.createElement("button");return t.type="button",t.className="btn ghost icon",t.textContent=e,t.disabled=o,t.setAttribute("aria-label",i),t.onclick=n,t}op(e){return this.write(e)}edit(e){let i=this.listField;if(!i)return;let o=e===null?null:this.items[e]??null,n=async(r)=>{if(o)return this.op({update:o.id,patch:r});return this.op({insert:r})},t=new CustomEvent("pixl-edit",{cancelable:!0,bubbles:!0,composed:!0,detail:{item:o,index:e,save:n}});if(!this.dispatchEvent(t))return;this.openBuiltInEditor(i,o,n)}openBuiltInEditor(e,i,o){let n=this.dialog;if(!n)return;let t={},r=document.createElement("form");r.className="form";for(let[c,p]of Object.entries(e.item)){t[c]=i?i[c]:p.default;let l=document.createElement("label");l.append(p.label);let d=v(p,t[c],{set:(g)=>t[c]=g},{label:p.label,widget:this.widget});l.append(d.el),r.append(l)}let s=document.createElement("button");s.type="button",s.className="btn",s.textContent="Abbrechen",s.slot="actions",s.onclick=()=>n.close();let a=document.createElement("button");a.type="button",a.className="btn primary",a.textContent=i?"Speichern":"Hinzufügen",a.slot="actions",a.onclick=async()=>{if(a.disabled=!0,await o(t))n.close();a.disabled=!1},r.onsubmit=(c)=>{c.preventDefault(),a.click()},n.setAttribute("heading",i?"Eintrag bearbeiten":"Neuer Eintrag"),n.replaceChildren(r,s,a),n.open()}}var Ne=`
270
+ h3 {
271
+ margin: 0 0 10px; padding: 0 4px; font-size: 12px; font-weight: 600; color: var(--muted);
272
+ text-transform: uppercase; letter-spacing: 0.04em;
273
+ }
274
+ .box { display: flex; flex-direction: column; gap: 10px; }
275
+ ::slotted(*) {
276
+ border: 1px solid var(--line); border-radius: 12px; background: var(--surface); padding: 0 16px;
277
+ }
278
+ `;class K extends HTMLElement{static observedAttributes=["heading"];root;constructor(){super();this.root=this.attachShadow({mode:"open"}),m(this.root,Ne),this.root.innerHTML='<h3 part="heading"></h3><div class="box" part="box"><slot></slot></div>',this.sync()}attributeChangedCallback(){this.sync()}sync(){let e=this.root.querySelector("h3");e.textContent=this.getAttribute("heading")??"",e.hidden=!e.textContent}}class G extends HTMLElement{static observedAttributes=["scope"];connectedCallback(){this.build()}attributeChangedCallback(){if(this.isConnected)this.build()}async build(){let e=await x(),i=this.getAttribute("scope")==="settings"?"settings":"controls",o=(i==="settings"?e?.manifest?.settings:e?.manifest?.controls)??{},n=new Map,t=new Map,r=[];for(let[a,c]of Object.entries(o)){if(c.hidden)continue;let p=c.group??"",l=n.get(p);if(!l)l=document.createElement("pixl-section"),l.style.marginBottom="24px",n.set(p,l),t.set(p,[]),r.push(l);t.get(p).push(c.label);let d=document.createElement(c.type==="list"?"pixl-list":"pixl-field");if(d.setAttribute("field",a),d.setAttribute("scope",i),c.type==="list")d.style.padding="12px 0";l.append(d)}let s=(a)=>a.trim().toLocaleLowerCase();for(let[a,c]of n){let p=t.get(a)??[],l=p.length===1&&s(p[0])===s(a);if(a&&!l)c.setAttribute("heading",a)}this.replaceChildren(...r)}}var Ie=[["pixl-field",b],["pixl-toggle",D],["pixl-counter",F],["pixl-range",R],["pixl-select",j],["pixl-text",W],["pixl-color",B],["pixl-file",z],["pixl-button",q],["pixl-list",_],["pixl-dialog",P],["pixl-dropdown",I],["pixl-checkbox",O],["pixl-section",K],["pixl-controls",G]];function de(){if(typeof customElements>"u")return;V();for(let[e,i]of Ie)if(!customElements.get(e))customElements.define(e,i)}var ce=globalThis.Pixl;if(!ce)console.error("[pixl] pixl-ui.js braucht pixl.js, das zuerst einbinden");else Y(ce);de();U();Object.assign(globalThis,{PixlUI:{pixl:M,widget:x,fullscreen:k,createInput:v,css:L}});})();
package/dist/pixl.js CHANGED
@@ -1,6 +1,6 @@
1
- (()=>{function S(t,e=1){let r=encodeURIComponent(t.id);switch(t.provider){case"twitch":return`https://static-cdn.jtvnw.net/emoticons/v2/${r}/${t.animated?"default":"static"}/dark/${e===4?3:e}.0`;case"7tv":return`https://cdn.7tv.app/emote/${r}/${e}x.webp`;case"bttv":return`https://cdn.betterttv.net/emote/${r}/${e===4?3:e}x.webp`;case"ffz":return`https://cdn.frankerfacez.com/emote/${r}/${e}`}}var M={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};function d(t){return t.replace(/[&<>"']/g,(e)=>M[e])}function b(t,e){let r=d(t.name);return`<img class="pixl-emote" src="${d(S(t,e))}" alt="${r}" data-provider="${t.provider}" data-emote="${d(t.id)}">`}function f(t,e={},r={}){let n=r.size??2,s=t.split(/(\s+)/),i=[],o=-1;for(let a of s){if(!a)continue;if(/^\s+$/.test(a)){i.push(d(a));continue}let l=Object.prototype.hasOwnProperty.call(e,a)?e[a]:void 0;if(!l){i.push(d(a)),o=-1;continue}if(l.zeroWidth&&o>=0){i.length=o+1;let u=i[o],c=u.startsWith('<span class="pixl-emote-stack">')?u.slice(31,-7):u;i[o]=`<span class="pixl-emote-stack">${c}${b(l,n)}</span>`;continue}i.push(b(l,n)),o=i.length-1}return i.join("")}function j(t=[]){return t.map((e)=>`<img class="pixl-badge" src="${d(e.url)}" alt="${d(e.title)}" data-provider="${e.provider}">`).join("")}var _=`.pixl-emote{height:1.75em;vertical-align:middle;margin:-.25em 0}
1
+ (()=>{function H(e){if(e.startsWith("twitch.")||e.startsWith("stream.")||e.startsWith("streamelements.")||e.startsWith("seventv."))return"events:read";if(e.startsWith("state."))return"states:read";if(e.startsWith("tts."))return"tts:read";if(e.startsWith("subs."))return"subs:read";if(e.startsWith("spotify."))return"spotify:read";return null}function T(e,t="123456789"){return{id:t,login:e.toLowerCase(),name:e,avatar:"https://static-cdn.jtvnw.net/user-default-pictures-uv/cdd517fe-def4-11e9-948e-784f43822e80-profile_image-300x300.png"}}var f=T("FrostByte","100000001"),h=T("Nordlicht","100000002"),y=T("SophiePlays","100000003"),b=T("JonasNRW","100000004"),W={level:2,progress:1200,goal:3000,total:4700,expiresAt:"2026-01-01T20:05:00.000Z",startedAt:"2026-01-01T20:00:00.000Z",type:"regular",shared:!1,participants:[],topContributions:[{user:f,type:"bits",total:2500},{user:h,type:"subscription",total:1500}],record:{level:5,total:18000}},_={id:"60ae958e229664e8667aea38",name:"peepoHey",provider:"7tv",animated:!0,zeroWidth:!1},w={"stream.online":{streamId:"40000000000",title:"Gemütlicher Abend mit euch",category:"Minecraft",url:"https://twitch.tv/pixl",thumbnailUrl:null},"stream.offline":{durationSec:12300},"twitch.follow":{user:f},"twitch.sub":{user:f,tier:"1",months:7,streak:4,durationMonths:1,message:"Sieben Monate schon!"},"twitch.sub_gift":{user:h,tier:"1",count:5,community:!0,months:1,recipients:[y,f],totalGifted:42},"twitch.cheer":{user:y,bits:500,message:"Cheer500 GG"},"twitch.raid":{user:h,viewers:87},"twitch.channel_point":{user:f,reward:"Hydrieren",cost:500,input:null},"twitch.channel_point_resolved":{user:f,reward:"Hydrieren",status:"fulfilled"},"twitch.power_up":{user:y,title:"Konfetti",bits:100,input:null},"twitch.watch_streak":{user:y,streak:10,channelPoints:450},"twitch.modiversary":{user:b,months:24},"twitch.shoutout_sent":{user:h,viewers:87},"twitch.poll_started":{title:"Welche Welt als Nächstes?",choices:[{title:"Nether",votes:0},{title:"End",votes:0}]},"twitch.poll_progress":{title:"Welche Welt als Nächstes?",choices:[{title:"Nether",votes:12},{title:"End",votes:9}]},"twitch.poll_ended":{title:"Welche Welt als Nächstes?",status:"completed",choices:[{title:"Nether",votes:31},{title:"End",votes:24}]},"twitch.prediction_started":{title:"Schafft er den Boss?",outcomes:[{title:"Ja",points:0},{title:"Nein",points:0}]},"twitch.prediction_progress":{title:"Schafft er den Boss?",outcomes:[{title:"Ja",points:12000},{title:"Nein",points:8000}]},"twitch.prediction_locked":{title:"Schafft er den Boss?",outcomes:[{title:"Ja",points:25000},{title:"Nein",points:19000}]},"twitch.prediction_ended":{title:"Schafft er den Boss?",status:"resolved",winner:"Ja",outcomes:[{title:"Ja",points:25000},{title:"Nein",points:19000}]},"twitch.hype_train_started":{...W,level:1,progress:400,goal:2000,total:400},"twitch.hype_train_progress":W,"twitch.hype_train_ended":{...W,expiresAt:null},"twitch.goal_started":{type:"follower",description:"Follower-Ziel",current:980,target:1000},"twitch.goal_progress":{current:995,target:1000},"twitch.goal_ended":{type:"follower",description:"Follower-Ziel",current:1003,target:1000,achieved:!0},"twitch.charity_started":{charity:"Ärzte ohne Grenzen",target:{value:1000,currency:"EUR"}},"twitch.charity_progress":{current:{value:420,currency:"EUR"},target:{value:1000,currency:"EUR"}},"twitch.charity_donated":{user:h,charity:"Ärzte ohne Grenzen",amount:{value:25,currency:"EUR"}},"twitch.charity_stopped":{charity:"Ärzte ohne Grenzen",current:{value:1240,currency:"EUR"},target:{value:1000,currency:"EUR"}},"twitch.ad_break":{durationSec:90,automatic:!0,startedAt:"2026-01-01T20:30:00.000Z"},"twitch.title_changed":{title:"Endlich das End"},"twitch.category_changed":{category:"Minecraft"},"twitch.ban":{user:h,moderator:b,reason:"Spam",permanent:!1,endsAt:"2026-01-01T20:10:00.000Z"},"twitch.unban":{user:h,moderator:b},"twitch.vip_added":{user:y},"twitch.vip_removed":{user:y},"twitch.moderator_added":{user:b},"twitch.moderator_removed":{user:b},"twitch.chat_message":{user:f,messageId:"00000000-0000-0000-0000-000000000001",text:"Hallo zusammen peepoHey",html:'Hallo zusammen <img class="pixl-emote" src="https://cdn.7tv.app/emote/60ae958e229664e8667aea38/2x.webp" alt="peepoHey" data-provider="7tv" data-emote="60ae958e229664e8667aea38">',color:"#8A5CF5",emotes:{peepoHey:_},badges:[],paint:null},"twitch.chat_cleared":{},"twitch.chat_user_cleared":{user:h},"twitch.chat_message_deleted":{user:h,messageId:"00000000-0000-0000-0000-000000000002"},"twitch.chat_settings_updated":{emoteMode:!1,followerMode:!0,followerModeDuration:10,slowMode:!0,slowModeWait:5,subscriberMode:!1,uniqueChatMode:!1},"twitch.chat_message_held":{user:h,text:"kauf billige follower",messageId:"00000000-0000-0000-0000-000000000003"},"twitch.chat_message_held_updated":{user:h,status:"denied",messageId:"00000000-0000-0000-0000-000000000003"},"twitch.shared_chat_started":{sessionId:"shared-1",participants:2},"twitch.shared_chat_updated":{sessionId:"shared-1",participants:3},"twitch.shared_chat_ended":{sessionId:"shared-1"},"twitch.guest_star_session_started":{sessionId:"guest-1"},"twitch.guest_star_session_ended":{sessionId:"guest-1"},"twitch.guest_star_guest_updated":{sessionId:"guest-1",user:y,state:"live",slotId:"1"},"twitch.guest_star_settings_updated":{slotCount:4,groupLayout:"tiled"},"seventv.emote_added":{emote:_,actor:"FrostByte"},"seventv.emote_removed":{emote:_,actor:"FrostByte"},"seventv.emote_renamed":{emote:_,oldName:"peepoHi",actor:"FrostByte"},"seventv.emote_set_changed":{setId:"01H000000000000000000000",name:"Kanal-Emotes",actor:"FrostByte"},"streamelements.tip":{name:"SophiePlays",amount:{value:5,currency:"EUR"},message:"Für den nächsten Kaffee",tipId:"tip-1"},"subs.updated":{total:312,points:340,at:"2026-01-01T20:00:00.000Z"},"spotify.now_playing":{playing:!0,progressMs:42000,at:"2026-01-01T20:00:00.000Z",track:null,addedBy:null},"tts.ready":{id:"00000000-0000-0000-0000-000000000004",voice:"Vicki",text:"SophiePlays hat 5 Euro gespendet: Für den nächsten Kaffee",source:"tip",eventId:null},"tts.skip":{id:null},"tts.clear":{}};function N(e,t=1){let i=encodeURIComponent(e.id);switch(e.provider){case"twitch":return`https://static-cdn.jtvnw.net/emoticons/v2/${i}/${e.animated?"default":"static"}/dark/${t===4?3:t}.0`;case"7tv":return`https://cdn.7tv.app/emote/${i}/${t}x.webp`;case"bttv":return`https://cdn.betterttv.net/emote/${i}/${t===4?3:t}x.webp`;case"ffz":return`https://cdn.frankerfacez.com/emote/${i}/${t}`}}var ce={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};function v(e){return e.replace(/[&<>"']/g,(t)=>ce[t])}function j(e,t){let i=v(e.name);return`<img class="pixl-emote" src="${v(N(e,t))}" alt="${i}" data-provider="${e.provider}" data-emote="${v(e.id)}">`}function ue(e,t={},i={}){let n=i.size??2,r=e.split(/(\s+)/),s=[],o=-1;for(let a of r){if(!a)continue;if(/^\s+$/.test(a)){s.push(v(a));continue}let d=Object.prototype.hasOwnProperty.call(t,a)?t[a]:void 0;if(!d){s.push(v(a)),o=-1;continue}if(d.zeroWidth&&o>=0){s.length=o+1;let m=s[o],p=m.startsWith('<span class="pixl-emote-stack">')?m.slice(31,-7):m;s[o]=`<span class="pixl-emote-stack">${p}${j(d,n)}</span>`;continue}s.push(j(d,n)),o=s.length-1}return s.join("")}function pe(e=[]){return e.map((t)=>`<img class="pixl-badge" src="${v(t.url)}" alt="${v(t.title)}" data-provider="${t.provider}">`).join("")}var he=`.pixl-emote{height:1.75em;vertical-align:middle;margin:-.25em 0}
2
2
  .pixl-emote-stack{display:inline-grid;vertical-align:middle}
3
3
  .pixl-emote-stack>.pixl-emote{grid-area:1/1;justify-self:center;align-self:center;margin:0}
4
- .pixl-badge{height:1.1em;vertical-align:middle;margin-right:.25em}`;function L(t){switch(t.type){case"linear-gradient":case"radial-gradient":{let e=t.stops.map((n)=>`${n.color} ${Math.round(n.at*1000)/10}%`).join(", "),r=`${t.repeating?"repeating-":""}${t.type}`;return t.type==="linear-gradient"?`${r}(${t.angle}deg, ${e})`:`${r}(${t.shape}, ${e})`}case"color":return`linear-gradient(${t.color}, ${t.color})`;case"image":return`url("${t.url.replace(/"/g,"%22")}")`}}function w(t,e="inherit"){let r=t.layers.filter((s)=>s.opacity>0);if(!r.length)return`color:${e}`;let n=t.shadows.map((s)=>`drop-shadow(${s.x}px ${s.y}px ${s.blur}px ${s.color})`).join(" ");return[`color:${e}`,`background-image:${r.map(L).join(", ")}`,"background-size:cover","-webkit-background-clip:text","background-clip:text","-webkit-text-fill-color:transparent",n?`filter:${n}`:""].filter(Boolean).join(";")}class g{pixl;paints=new Map;queue=new Map;timer=null;constructor(t){this.pixl=t}css=_;emoteUrl(t,e=1){return S(t,e)}badgesHtml(t){return j(t)}paintCss(t,e){return w(t,e)}html(t,e,r){return f(t,e,r)}async emotes(){return(await this.pixl.request("/emotes")).emotes}paint(t){let e=this.paints.get(t);if(!e)e=new Promise((r)=>{this.queue.set(t,r),this.timer??=setTimeout(()=>this.flush(),20)}),this.paints.set(t,e);return e}async paintStyle(t,e="inherit"){let r=t?await this.paint(t):null;return r?w(r,e):`color:${e}`}async flush(){this.timer=null;let t=this.queue;this.queue=new Map;let e=[...t.keys()],r=new Map;try{for(let n=0;n<e.length;n+=100){let s=await this.pixl.request("/7tv/paints",{ids:e.slice(n,n+100).join(",")});for(let i of s.paints)r.set(i.id,i)}}catch(n){console.warn("[pixl] 7TV-Paints nicht geladen:",n);for(let s of e)this.paints.delete(s)}for(let[n,s]of t)s(r.get(n)??null)}}var T=[2000,5000,1e4,30000];class v{ready=!1;error=null;pixl;since;types;handler;delivered=new Set;errorListeners=new Set;buffer=null;loadSeq=0;stopped=!1;latestAt=null;retryTimer=null;retryAttempt=0;unsubscribeStatus;onEvent=(t,e)=>this.handleLive(e);constructor(t,e,r){if(e?.since===void 0)throw Error("pixl.feed: `since` fehlt");this.pixl=t,this.since=e.since instanceof Date?e.since.toISOString():String(e.since),this.types=e.types?.length?new Set(e.types):null,this.handler=r,t.on("*",this.onEvent),this.unsubscribeStatus=t.onStatus((n)=>{if(n==="connected")this.retryAttempt=0,this.load()})}onError(t){if(this.errorListeners.add(t),this.error)t(this.error);return()=>this.errorListeners.delete(t)}stop(){this.stopped=!0,this.clearRetry(),this.pixl.off("*",this.onEvent),this.unsubscribeStatus(),this.errorListeners.clear(),this.buffer=null}async load(){let t=++this.loadSeq;this.clearRetry(),this.buffer=[];let e=null;try{let n=await this.fetchHistory();if(t!==this.loadSeq||this.stopped)return;this.error=null,this.retryAttempt=0;for(let s of n)this.deliver({event:s.event,data:s.data,replay:!!(s.data?.replay||s.data?.test),id:s.id,at:s.at,history:!0})}catch(n){if(t!==this.loadSeq||this.stopped)return;e=n instanceof Error?n:Error(String(n)),this.error=e;for(let s of this.errorListeners)s(e)}let r=this.buffer??[];this.buffer=null,this.ready=!0;for(let n of r)this.deliver(n);if(e)this.scheduleRetry(e)}async fetchHistory(){let t=this.latestAt===null?null:this.latestAt-300000,e=[],r;do{let n=await this.pixl.events({since:this.since,types:this.types?[...this.types]:void 0,limit:200,cursor:r});e.push(...n.events),r=n.nextCursor??void 0;let s=n.events[n.events.length-1];if(t!==null&&s&&Date.parse(s.at)<t)break}while(r);return e.reverse()}scheduleRetry(t){let e=t.status;if(e!==void 0&&e>=400&&e<500&&e!==429&&e!==408)return;if(this.pixl.status!=="connected")return;let r=T[Math.min(this.retryAttempt,T.length-1)];this.retryAttempt+=1,this.retryTimer=setTimeout(()=>{if(this.retryTimer=null,!this.stopped&&this.pixl.status==="connected")this.load()},r+Math.random()*r*0.3)}clearRetry(){if(this.retryTimer)clearTimeout(this.retryTimer);this.retryTimer=null}handleLive(t){if(this.stopped)return;if(this.types&&!this.types.has(t.event))return;if(this.buffer){this.buffer.push(t);return}this.deliver(t)}deliver(t){if(t.id){if(this.delivered.has(t.id))return;this.delivered.add(t.id)}if(t.at){let e=Date.parse(t.at);if(!Number.isNaN(e)&&(this.latestAt===null||e>this.latestAt))this.latestAt=e}try{this.handler(t.data,t)}catch(e){console.error("[pixl] Fehler im feed()-Handler:",e)}}}class x{pixl;constructor(t){this.pixl=t}async speak(t,e={}){return await(await this.pixl._fetch("/tts",{method:"POST",json:{text:t,voice:e.voice,filters:e.filters}})).json()}async voices(){return await(await this.pixl._fetch("/tts/voices")).json()}async audio(t){return(await this.pixl._fetch(`/tts/${encodeURIComponent(t)}/audio`)).blob()}async marks(t){return(await(await this.pixl._fetch(`/tts/${encodeURIComponent(t)}/marks`)).json()).marks}player(t={}){return new k(this.pixl,this,t)}}class k{pixl;tts;options;queue=[];current=null;wordFrame=null;volume;destroyed=!1;warnedAutoplay=!1;onReady;onSkip;onClear;constructor(t,e,r){this.pixl=t;this.tts=e;this.options=r;this.volume=P(r.volume??1),this.onReady=(n,s)=>{let i=F(n,s);if(!i||s.replay&&this.options.ignoreReplays)return;if(this.current?.item.id===i.id||this.queue.some((a)=>a.id===i.id))return;this.queue.push(i);let o=this.options.maxQueue??20;if(this.queue.length>o)this.queue.splice(0,this.queue.length-o);this.next()},this.onSkip=(n)=>{let s=typeof n.id==="string"?n.id:void 0;if(!s||this.current?.item.id===s)this.skip();else this.queue=this.queue.filter((i)=>i.id!==s)},this.onClear=()=>this.clear(),t.on("tts.ready",this.onReady),t.on("tts.skip",this.onSkip),t.on("tts.clear",this.onClear)}skip(){if(!this.current)return;this.current.skipped=!0,this.current.finish()}clear(){this.queue=[],this.skip()}setVolume(t){if(this.volume=P(t),this.current?.audio)this.current.audio.volume=this.volume}get pending(){return this.queue}destroy(){this.destroyed=!0,this.pixl.off("tts.ready",this.onReady),this.pixl.off("tts.skip",this.onSkip),this.pixl.off("tts.clear",this.onClear),this.clear()}followWords(t,e,r){this.stopWords();let n=0,s=(o)=>r[o].end??r[o+1]?.start??(Number.isFinite(t.duration)?Math.round(t.duration*1000):r[o].start),i=()=>{let o=t.currentTime*1000;while(n<r.length&&r[n].start<=o){let a=n++,l={word:r[a].word,index:a,start:r[a].start,end:s(a)};y(()=>this.options.onWord?.(l,e))}this.wordFrame=n<r.length&&!t.ended?E(i):null};this.wordFrame=E(i)}stopWords(){if(this.wordFrame!==null)I(this.wordFrame);this.wordFrame=null}async next(){if(this.current||this.destroyed)return;let t=this.queue.shift();if(!t)return;let e=!1,r,n=new Promise((i)=>r=i),s={item:t,audio:null,url:null,skipped:!1,finish:()=>{if(e)return;if(e=!0,this.stopWords(),s.audio?.pause(),s.url)URL.revokeObjectURL(s.url);r()}};this.current=s;try{let[i,o]=await Promise.all([this.tts.audio(t.id),this.options.onWord?this.tts.marks(t.id).catch((a)=>{console.warn("[pixl] Zeitmarken nicht verfügbar, onWord bleibt für diesen Eintrag stumm:",a);return}):Promise.resolve(void 0)]);if(o)t.marks=o;if(!e){s.url=URL.createObjectURL(i);let a=new Audio(s.url);if(a.volume=this.volume,a.addEventListener("ended",s.finish),a.addEventListener("error",s.finish),s.audio=a,y(()=>this.options.onStart?.(t)),o?.length)this.followWords(a,t,o);await a.play().catch((l)=>{if(!this.warnedAutoplay&&l instanceof DOMException&&l.name==="NotAllowedError")this.warnedAutoplay=!0,console.warn("[pixl] Der Browser blockiert die Wiedergabe ohne Klick. In OBS und StreamElements passiert das nicht, im normalen Browser einmal auf die Seite klicken.");s.finish()})}}catch(i){console.error("[pixl] TTS-Audio konnte nicht geladen werden:",i),s.finish()}if(await n,y(()=>this.options.onEnd?.(t,{skipped:s.skipped})),this.current=null,this.queue.length&&!this.destroyed)setTimeout(()=>void this.next(),this.options.gapMs??500)}}function E(t){return typeof requestAnimationFrame==="function"?requestAnimationFrame(t):setTimeout(t,30)}function I(t){if(typeof cancelAnimationFrame==="function")cancelAnimationFrame(t);else clearTimeout(t)}function F(t,e){if(typeof t.id!=="string")return null;return{id:t.id,text:typeof t.text==="string"?t.text:"",voice:typeof t.voice==="string"?t.voice:"",source:typeof t.source==="string"?t.source:"",...typeof t.eventId==="string"?{eventId:t.eventId}:{},...t.textTruncated===!0?{textTruncated:!0}:{},replay:e.replay}}function P(t){return Number.isFinite(t)?Math.min(1,Math.max(0,t)):1}function y(t){try{t()}catch(e){console.error("[pixl] Fehler in einem TTS-Handler:",e)}}class h extends Error{status;problem;constructor(t,e,r){super(e);this.name="PixlApiError",this.status=t,this.problem=r}}var q="https://pixl.lennox.zip",C=[500,1000,2000,3000,5000,1e4],U=45000,W=15000;function R(t){if(t instanceof Date)return t.toISOString();return String(t)}class p{static defaultBaseUrl;baseUrl;key;tts;chat;handlers=new Map;stateHandlers=new Map;statusHandlers=new Set;states=new Map;_status="closed";abort=null;reconnectTimer=null;watchdog=null;attempt=0;stopped=!1;logs;everConnected=!1;lostAt=null;constructor(t){if(!t?.key)throw Error("pixl: `key` fehlt (API-Schlüssel aus den Einstellungen)");if(this.key=t.key,this.logs=t.logs!==!1,this.baseUrl=(t.baseUrl??p.defaultBaseUrl??q).replace(/\/+$/,""),this.tts=new x(this),this.chat=new g(this),t.autoConnect!==!1)queueMicrotask(()=>this.connect())}on(t,e){let r=this.handlers.get(t);if(!r)this.handlers.set(t,r=new Set);return r.add(e),this}off(t,e){return this.handlers.get(t)?.delete(e),this}state(t,e){let r=this.stateHandlers.get(t);if(!r)this.stateHandlers.set(t,r=new Set);if(r.add(e),this.states.has(t))e(this.states.get(t),t);return this}offState(t,e){return this.stateHandlers.get(t)?.delete(e),this}getState(t){return this.states.get(t)}get status(){return this._status}onStatus(t){return this.statusHandlers.add(t),t(this._status),()=>this.statusHandlers.delete(t)}setStatus(t){if(t===this._status)return;this._status=t;for(let e of this.statusHandlers)m(()=>e(t))}async request(t,e={}){let r=new URL(`${this.baseUrl}/api/v1${t}`);for(let[s,i]of Object.entries(e))if(i!==void 0&&i!=="")r.searchParams.set(s,i);let n=await fetch(r,{headers:{Authorization:`Bearer ${this.key}`}});if(!n.ok){let s=await n.json().catch(()=>null);throw new h(n.status,s?.error??`HTTP ${n.status}`)}return await n.json()}async _fetch(t,e={}){let r={Authorization:`Bearer ${this.key}`};if(e.json!==void 0)r["Content-Type"]="application/json";let n=await fetch(`${this.baseUrl}/api/v1${t}`,{method:e.method??"GET",headers:r,body:e.json!==void 0?JSON.stringify(e.json):void 0});if(!n.ok){let s=await n.json().catch(()=>null);throw new h(n.status,s?.error??`HTTP ${n.status}`,s?.problem)}return n}events(t={}){return this.request("/events",{since:t.since!==void 0?R(t.since):void 0,until:t.until!==void 0?R(t.until):void 0,types:t.types?.join(","),limit:t.limit!==void 0?String(t.limit):void 0,cursor:t.cursor})}org(){return this.request("/org")}subs(){return this.request("/subs")}nowPlaying(){return this.request("/spotify/now-playing")}static progress(t,e=Date.now()){if(!t.track)return 0;let r=t.playing?t.progressMs+(e-Date.parse(t.at)):t.progressMs;return Math.max(0,Math.min(r,t.track.durationMs))}streamElements(t,e={}){let r={};for(let[n,s]of Object.entries(e))r[n]=s===void 0?void 0:String(s);return this.request(`/streamelements/${t}`,r)}async fetchStates(){return(await this.request("/states")).states}async fetchAllEvents(t={}){let e=[],r;do{let n=await this.events({...t,limit:200,cursor:r});e.push(...n.events),r=n.nextCursor??void 0}while(r);return e.reverse()}feed(t,e){return new v(this,t,e)}connect(){if(this.abort)return;this.stopped=!1,this.run()}disconnect(){if(this.stopped=!0,this.reconnectTimer)clearTimeout(this.reconnectTimer);this.reconnectTimer=null,this.abort?.abort(),this.abort=null,this.setStatus("closed")}async run(){let t=new AbortController;this.abort=t,this.setStatus("connecting");let e=!1;try{this.watchdog=setTimeout(()=>t.abort(),W);let r=await fetch(`${this.baseUrl}/api/v1/stream`,{headers:{Authorization:`Bearer ${this.key}`,Accept:"text/event-stream"},signal:t.signal,cache:"no-store"});if(this.clearWatchdog(),r.status===401||r.status===403){e=!0;return}if(!r.ok||!r.body)throw Error(`HTTP ${r.status}`);if(this.attempt=0,this.lostAt!==null)this.log(`Live-Verbindung wieder da nach ${((Date.now()-this.lostAt)/1000).toFixed(1)} s`);this.lostAt=null,this.everConnected=!0,this.setStatus("connected"),await this.readStream(r.body,t,()=>{e=!0})}catch{}finally{if(this.clearWatchdog(),this.abort===t)this.abort=null;if(e)this.log("Live-Verbindung abgelehnt (Schlüssel ungültig oder widerrufen), kein neuer Versuch"),this.stopped=!0,this.setStatus("unauthorized");else if(!this.stopped){if(this.lostAt===null)this.lostAt=Date.now(),this.log(this.everConnected?"Live-Verbindung getrennt, verbinde neu":"Live-Verbindung nicht erreichbar, versuche es weiter");this.setStatus("disconnected"),this.scheduleReconnect()}}}log(t){if(this.logs)console.info(`[pixl] ${t}`)}scheduleReconnect(){let t=C[Math.min(this.attempt,C.length-1)];this.attempt+=1;let e=t+Math.random()*t*0.3;this.reconnectTimer=setTimeout(()=>{if(this.reconnectTimer=null,!this.stopped)this.connect()},e)}armWatchdog(t){this.clearWatchdog(),this.watchdog=setTimeout(()=>t.abort(),U)}clearWatchdog(){if(this.watchdog)clearTimeout(this.watchdog);this.watchdog=null}async readStream(t,e,r){let n=t.getReader(),s=new TextDecoder,i="",o=[];this.armWatchdog(e);for(;;){let{value:a,done:l}=await n.read();if(l)return;this.armWatchdog(e),i+=s.decode(a,{stream:!0});let u;while((u=i.indexOf(`
5
- `))>=0){let c=i.slice(0,u).replace(/\r$/,"");if(i=i.slice(u+1),c===""){if(o.length){let O=o.join(`
6
- `);if(o=[],this.dispatch(O)==="revoked"){r(),await n.cancel().catch(()=>{});return}}}else if(c.startsWith("data:"))o.push(c.slice(5).replace(/^ /,""))}}}dispatch(t){let e;try{e=JSON.parse(t)}catch{return}if(e.event==="api_key.revoked")return"revoked";if(e.event==="state.snapshot"){let n=e.data?.states??[];for(let{key:s,value:i}of n)this.setState(s,i);return}if(e.event==="state.updated"){let{key:n,value:s}=e.data;if(typeof n==="string")this.setState(n,s);return}let r={event:e.event,data:e.data??{},replay:!!e.replay,id:e.id,at:e.at};this.emit(r)}emit(t){if(t.event==="twitch.chat_message"&&typeof t.data.text==="string"&&t.data.html===void 0)t.data.html=f(t.data.text,t.data.emotes);for(let e of this.handlers.get(t.event)??[])m(()=>e(t.data,t));for(let e of this.handlers.get("*")??[])m(()=>e(t.data,t))}setState(t,e){this.states.set(t,e);for(let r of this.stateHandlers.get(t)??[])m(()=>r(e,t))}}function m(t){try{t()}catch(e){console.error("[pixl] Fehler in einem Handler:",e)}}function A(t){if(!t)return;try{let e=new URL(t);return e.pathname.replace(/\/+$/,"").endsWith("/sdk/pixl.js")?e.origin:void 0}catch{return}}var D=typeof document<"u"?document.currentScript:null,H=A(D?.src);if(H)p.defaultBaseUrl=H;Object.assign(globalThis,{Pixl:p,PixlApiError:h});})();
4
+ .pixl-badge{height:1.1em;vertical-align:middle;margin-right:.25em}`;function ge(e){switch(e.type){case"linear-gradient":case"radial-gradient":{let t=e.stops.map((n)=>`${n.color} ${Math.round(n.at*1000)/10}%`).join(", "),i=`${e.repeating?"repeating-":""}${e.type}`;return e.type==="linear-gradient"?`${i}(${e.angle}deg, ${t})`:`${i}(${e.shape}, ${t})`}case"color":return`linear-gradient(${e.color}, ${e.color})`;case"image":return`url("${e.url.replace(/"/g,"%22")}")`}}function q(e,t="inherit"){let i=e.layers.filter((r)=>r.opacity>0);if(!i.length)return`color:${t}`;let n=e.shadows.map((r)=>`drop-shadow(${r.x}px ${r.y}px ${r.blur}px ${r.color})`).join(" ");return[`color:${t}`,`background-image:${i.map(ge).join(", ")}`,"background-size:cover","-webkit-background-clip:text","background-clip:text","-webkit-text-fill-color:transparent",n?`filter:${n}`:""].filter(Boolean).join(";")}class I{pixl;paints=new Map;queue=new Map;timer=null;constructor(e){this.pixl=e}css=he;emoteUrl(e,t=1){return N(e,t)}badgesHtml(e){return pe(e)}paintCss(e,t){return q(e,t)}html(e,t,i){return ue(e,t,i)}async emotes(){return(await this.pixl.request("/emotes")).emotes}paint(e){let t=this.paints.get(e);if(!t)t=new Promise((i)=>{this.queue.set(e,i),this.timer??=setTimeout(()=>this.flush(),20)}),this.paints.set(e,t);return t}async paintStyle(e,t="inherit"){let i=e?await this.paint(e):null;return i?q(i,t):`color:${t}`}async flush(){this.timer=null;let e=this.queue;this.queue=new Map;let t=[...e.keys()],i=new Map;try{for(let n=0;n<t.length;n+=100){let r=await this.pixl.request("/7tv/paints",{query:{ids:t.slice(n,n+100).join(",")}});for(let s of r.paints)i.set(s.id,s)}}catch(n){console.warn("[pixl] 7TV-Paints nicht geladen:",n);for(let r of t)this.paints.delete(r)}for(let[n,r]of e)r(i.get(n)??null)}}class l extends Error{code;status;scope;problem;constructor(e,t,i={}){super(t);this.name="PixlError",this.code=e,this.status=i.status,this.scope=i.scope,this.problem=i.problem}}async function z(e){let t=await e.json().catch(()=>null),i=t?.error??`HTTP ${e.status}`,n={status:e.status,problem:t?.problem,scope:t?.scope};if(t?.code==="module_disabled")return new l("module_disabled",i,n);if(t?.code==="missing_scope"||t?.scope)return new l("missing_scope",i,n);switch(e.status){case 400:case 422:return new l("invalid",i,n);case 401:return new l("unauthorized",i,n);case 403:return new l("forbidden",i,n);case 404:return new l("not_found",i,n);case 429:return new l("rate_limited",i,n);default:return new l("server",i,n)}}class c{current;listeners=new Set;constructor(e){this.current=e}get value(){return this.current}subscribe(e){return this.listeners.add(e),u(()=>e(this.current)),()=>{this.listeners.delete(e)}}set(e){if(Object.is(e,this.current))return;this.current=e;for(let t of[...this.listeners])u(()=>t(e))}clear(){this.listeners.clear()}}function u(e){try{e()}catch(t){console.error("[pixl] Fehler in einem Handler:",t)}}var me=300000,K=[2000,5000,1e4,30000],G=5000;class L{pixl;events;handler;ready;error;readyValue=new c(!1);errorValue=new c(null);since;types;delivered=new Set;buffer=null;loadSeq=0;stopped=!1;latestAt=null;retryTimer=null;retryAttempt=0;unsubscribe=[];constructor(e,t,i,n){this.pixl=e;this.events=t;this.handler=n;if(i?.since===void 0)throw new l("invalid","pixl.events.feed: `since` fehlt");this.ready=this.readyValue,this.error=this.errorValue,this.since=i.since instanceof Date?i.since.toISOString():String(i.since),this.types=i.types?.length?new Set(i.types):null,this.unsubscribe.push(e.on("*",(r)=>this.handleLive(r))),this.unsubscribe.push(e.status.subscribe((r)=>{if(r==="connected")this.retryAttempt=0,this.load()}))}stop(){this.stopped=!0,this.clearRetry();for(let e of this.unsubscribe)e();this.buffer=null}async load(){let e=++this.loadSeq;this.clearRetry(),this.buffer=[];let t=null;try{let n=await this.fetchHistory();if(e!==this.loadSeq||this.stopped)return;this.errorValue.set(null),this.retryAttempt=0;for(let r of n)this.deliver(r)}catch(n){if(e!==this.loadSeq||this.stopped)return;t=n instanceof l?n:new l("network",String(n)),this.errorValue.set(t)}let i=this.buffer??[];this.buffer=null,this.readyValue.set(!0);for(let n of i)this.deliver(n);if(t)this.scheduleRetry(t)}async fetchHistory(){let e=this.latestAt===null?null:this.latestAt-me,t=[],i,n={since:this.since,types:this.types?[...this.types]:void 0};do{let r=await this.events.fetchPage(n,i);t.push(...r.events),i=r.nextCursor??void 0;let s=r.events[r.events.length-1];if(e!==null&&s&&Date.parse(s.at)<e)break}while(i);return t.reverse()}scheduleRetry(e){if(e.code!=="network"&&e.code!=="rate_limited"&&e.code!=="server")return;if(this.pixl.status.value!=="connected")return;let t=K[Math.min(this.retryAttempt,K.length-1)];this.retryAttempt+=1,this.retryTimer=setTimeout(()=>{if(this.retryTimer=null,!this.stopped&&this.pixl.status.value==="connected")this.load()},t+Math.random()*t*0.3)}clearRetry(){if(this.retryTimer)clearTimeout(this.retryTimer);this.retryTimer=null}handleLive(e){if(this.stopped)return;if(this.types&&!this.types.has(e.type))return;if(this.buffer){this.buffer.push(e);return}this.deliver(e)}deliver(e){if(e.id){if(this.delivered.has(e.id))return;if(this.delivered.add(e.id),this.delivered.size>G){let i=this.delivered.size-G,n=0;for(let r of this.delivered){if(n++>=i)break;this.delivered.delete(r)}}}let t=Date.parse(e.at);if(!Number.isNaN(t)&&e.source!=="test"&&(this.latestAt===null||t>this.latestAt))this.latestAt=t;u(()=>this.handler(e))}}function Z(e){return e instanceof Date?e.toISOString():String(e)}class M{pixl;constructor(e){this.pixl=e}async*list(e){let t;do{let i=await this.fetchPage(e,t);yield*i.events,t=i.nextCursor??void 0}while(t)}async all(e){let t=[];for await(let i of this.list(e))t.push(i);return t.reverse()}feed(e,t){return new L(this.pixl,this,e,t)}fetchPage(e,t){if(this.pixl.demo)return Promise.resolve({events:[],nextCursor:null});return this.pixl.request("/events",{query:{v:2,since:Z(e.since),until:e.until!==void 0?Z(e.until):void 0,types:e.types?.join(","),limit:200,cursor:t},signal:e.signal})}}var ye=[100,200,300,400,500,600,700,800,900].flatMap((e)=>[`${e}`,`${e}i`]).join(",");function fe(e){return`https://fonts.bunny.net/css?family=${encodeURIComponent(e.trim())}:${ye}&display=swap`}function x(e,t="sans-serif"){let i=typeof e==="string"?e.trim():"";if(!i)return t;return`"${i.replace(/["\\]/g,"\\$&")}", ${t}`}var Y=new Map;function P(e){let t=typeof e==="string"?e.trim():"";if(!t||typeof document>"u")return Promise.resolve(!1);let i=Y.get(t);if(!i)i=new Promise((n)=>{let r=document.createElement("link");r.rel="stylesheet",r.href=fe(t),r.dataset.pixlFont=t,r.onload=()=>{let s=document.fonts;if(!s)return n(!0);s.load(`16px ${x(t)}`).then((o)=>n(o.length>0),()=>n(!1))},r.onerror=()=>n(!1),document.head.append(r)}),Y.set(t,i);return i}function J(e){let t=new Set,i=e?.manifest;if(!e||!i)return[];let n=(r,s)=>{for(let[o,a]of Object.entries(r??{})){let d=s[o];if(a.type==="font"&&typeof d==="string")t.add(d.trim());if(a.type!=="list"||!Array.isArray(d))continue;let m=Object.entries(a.item).filter(([,p])=>p.type==="font").map(([p])=>p);if(!m.length)continue;for(let p of d)for(let S of m)if(typeof p?.[S]==="string")t.add(p[S].trim())}};return n(i.settings,e.settings),n(i.controls,e.values),t.delete(""),[...t]}function Q(){if(typeof window>"u"||window.parent===window)return;let e=globalThis;if(e.__pixlFrame)return;e.__pixlFrame=!0,window.addEventListener("message",(n)=>{if(n.source!==window.parent||typeof n.data!=="object"||!n.data)return;if(n.data.type!=="pixl:theme"||typeof n.data.vars!=="object")return;let r=document.documentElement;for(let[s,o]of Object.entries(n.data.vars))if(s.startsWith("--pixl-"))r.style.setProperty(s,String(o));if(n.data.scheme)r.style.colorScheme=n.data.scheme});let t=()=>{let n=document.body,r=getComputedStyle(n),s=n.getBoundingClientRect().height+parseFloat(r.marginTop)+parseFloat(r.marginBottom);window.parent.postMessage({type:"pixl:height",height:Math.ceil(s)},"*")},i=()=>{if(typeof ResizeObserver<"u")new ResizeObserver(t).observe(document.body);t()};if(document.body)i();else document.addEventListener("DOMContentLoaded",i,{once:!0});window.addEventListener("load",t)}class A{pixl;nowPlaying;value=new c(null);constructor(e){this.pixl=e;this.nowPlaying=this.value,e._onConnect(()=>this.load()),e._listen("spotify.now_playing",(t)=>this.value.set(t.data))}progress(e=Date.now()){let t=this.value.value;if(!t?.track)return 0;let i=t.playing?t.progressMs+(e-Date.parse(t.at)):t.progressMs;return Math.max(0,Math.min(i,t.track.durationMs))}async load(){if(this.pixl.demo){this.value.set({...w["spotify.now_playing"],at:new Date().toISOString()});return}if(!this.pixl.scopes?.includes("spotify:read"))return;try{this.value.set(await this.pixl.request("/spotify/now-playing"))}catch(e){if(e instanceof l&&e.code==="module_disabled")this.value.set(null)}}_close(){this.value.clear()}}class F{pixl;constructor(e){this.pixl=e}get(e,t={},i={}){return this.pixl.request(`/streamelements/${e}`,{query:t,signal:i.signal})}}var X=[500,1000,2000,3000,5000,1e4];class R{options;abort=null;reconnectTimer=null;watchdog=null;attempt=0;stopped=!0;everConnected=!1;lostAt=null;constructor(e){this.options=e}start(){if(this.abort)return;this.stopped=!1,this.run()}stop(){if(this.stopped=!0,this.reconnectTimer)clearTimeout(this.reconnectTimer);this.reconnectTimer=null,this.clearWatchdog(),this.abort?.abort(),this.abort=null,this.options.onStatus("closed")}async run(){let e=new AbortController;this.abort=e,this.options.onStatus("connecting");let t=!1;try{this.watchdog=setTimeout(()=>e.abort(),15000);let i=await(this.options.fetch??fetch)(this.options.url,{headers:{Authorization:`Bearer ${this.options.token()}`,Accept:"text/event-stream"},signal:e.signal,cache:"no-store"});if(this.clearWatchdog(),i.status===401||i.status===403){t=!0;return}if(!i.ok||!i.body)throw Error(`HTTP ${i.status}`);if(this.attempt=0,this.lostAt!==null)this.options.log(`Live-Verbindung wieder da nach ${((Date.now()-this.lostAt)/1000).toFixed(1)} s`);this.lostAt=null,this.everConnected=!0,await this.read(i.body,e,()=>{t=!0})}catch{}finally{if(this.clearWatchdog(),this.abort===e)this.abort=null;if(t)this.options.log("Live-Verbindung abgelehnt (Zugang ungültig oder widerrufen), kein neuer Versuch"),this.stopped=!0,this.options.onStatus("unauthorized");else if(!this.stopped){if(this.lostAt===null)this.lostAt=Date.now(),this.options.log(this.everConnected?"Live-Verbindung getrennt, verbinde neu":"Live-Verbindung nicht erreichbar, versuche es weiter");this.options.onStatus("disconnected"),this.scheduleReconnect()}}}scheduleReconnect(){let e=X[Math.min(this.attempt,X.length-1)];this.attempt+=1;let t=e+Math.random()*e*0.3;this.reconnectTimer=setTimeout(()=>{if(this.reconnectTimer=null,!this.stopped)this.start()},t)}armWatchdog(e){this.clearWatchdog(),this.watchdog=setTimeout(()=>e.abort(),45000)}clearWatchdog(){if(this.watchdog)clearTimeout(this.watchdog);this.watchdog=null}async read(e,t,i){let n=e.getReader(),r=new TextDecoder,s="",o=[];this.armWatchdog(t);for(;;){let{value:a,done:d}=await n.read();if(d)return;this.armWatchdog(t),s+=r.decode(a,{stream:!0});let m;while((m=s.indexOf(`
5
+ `))>=0){let p=s.slice(0,m).replace(/\r$/,"");if(s=s.slice(m+1),p===""){if(!o.length)continue;let S=o.join(`
6
+ `);o=[];let C;try{C=JSON.parse(S)}catch{continue}if(C.type==="access_revoked"){i(),await n.cancel().catch(()=>{});return}this.options.onMessage(C)}else if(p.startsWith("data:"))o.push(p.slice(5).replace(/^ /,""))}}}}class O{pixl;constructor(e){this.pixl=e}async speak(e,t={}){let i=await this.pixl.fetchRaw(this.base,{method:"POST",body:{text:e,voice:t.voice,filters:t.filters},signal:t.signal});if(i.status===204)throw new l("forbidden","Liest die Browserquelle vor, die Vorschau spielt es mit ab",{status:204,problem:"preview"});let n=await i.json();if(this.pixl._preview)this.pixl.sendTestEvent("tts.ready",{id:n.id,text:n.text,voice:n.voice,source:"api",eventId:null});return n}get base(){return this.pixl._preview?"/widget/preview/tts":"/tts"}voices(e={}){return this.pixl.request("/tts/voices",{signal:e.signal})}async audio(e,t={}){return(await this.pixl.fetchRaw(`${this.base}/${encodeURIComponent(e)}/audio`,{signal:t.signal})).blob()}async marks(e,t={}){return(await this.pixl.request(`${this.base}/${encodeURIComponent(e)}/marks`,{signal:t.signal})).marks}player(e={}){return new ie(this.pixl,this,e)}}class ie{pixl;tts;options;queue=[];current=null;wordFrame=null;volume;destroyed=!1;warnedAutoplay=!1;unsubscribe;constructor(e,t,i){this.pixl=e;this.tts=t;this.options=i;this.volume=te(i.volume??1);let n=(s)=>{let o=we(s);if(!o||o.replay&&this.options.ignoreReplays)return;if(this.current?.item.id===o.id||this.queue.some((d)=>d.id===o.id))return;this.queue.push(o);let a=this.options.maxQueue??20;if(this.queue.length>a)this.queue.splice(0,this.queue.length-a);this.next()},r=(s)=>{let o=s.data.id;if(!o||this.current?.item.id===o)this.skip();else this.queue=this.queue.filter((a)=>a.id!==o)};this.unsubscribe=[e._listen("tts.ready",n),e._listen("tts.skip",r),e._listen("tts.clear",()=>this.clear())]}skip(){if(!this.current)return;this.current.skipped=!0,this.current.finish()}clear(){this.queue=[],this.skip()}setVolume(e){if(this.volume=te(e),this.current?.audio)this.current.audio.volume=this.volume}get pending(){return this.queue}destroy(){this.destroyed=!0;for(let e of this.unsubscribe)e();this.clear()}followWords(e,t,i){this.stopWords();let n=0,r=(o)=>i[o].end??i[o+1]?.start??(Number.isFinite(e.duration)?Math.round(e.duration*1000):i[o].start),s=()=>{let o=e.currentTime*1000;while(n<i.length&&i[n].start<=o){let a=n++,d={word:i[a].word,index:a,start:i[a].start,end:r(a)};u(()=>this.options.onWord?.(d,t))}this.wordFrame=n<i.length&&!e.ended?ee(s):null};this.wordFrame=ee(s)}stopWords(){if(this.wordFrame!==null)ve(this.wordFrame);this.wordFrame=null}async next(){if(this.current||this.destroyed)return;let e=this.queue.shift();if(!e)return;let t=!1,i,n=new Promise((s)=>i=s),r={item:e,audio:null,url:null,skipped:!1,finish:()=>{if(t)return;if(t=!0,this.stopWords(),r.audio?.pause(),r.url)URL.revokeObjectURL(r.url);i()}};this.current=r;try{let[s,o]=await Promise.all([this.tts.audio(e.id),this.options.onWord?this.tts.marks(e.id).catch((a)=>{console.warn("[pixl] Zeitmarken nicht verfügbar, onWord bleibt für diesen Eintrag stumm:",a);return}):Promise.resolve(void 0)]);if(o)e.marks=o;if(!t){r.url=URL.createObjectURL(s);let a=new Audio(r.url);if(a.volume=this.volume,a.addEventListener("ended",r.finish),a.addEventListener("error",r.finish),r.audio=a,u(()=>this.options.onStart?.(e)),o?.length)this.followWords(a,e,o);await a.play().catch((d)=>{if(!this.warnedAutoplay&&d instanceof DOMException&&d.name==="NotAllowedError")this.warnedAutoplay=!0,console.warn("[pixl] Der Browser blockiert die Wiedergabe ohne Klick. In OBS und StreamElements passiert das nicht, im normalen Browser einmal auf die Seite klicken.");r.finish()})}}catch(s){console.error("[pixl] TTS-Audio konnte nicht geladen werden:",s),r.finish()}if(await n,u(()=>this.options.onEnd?.(e,{skipped:r.skipped})),this.current=null,this.queue.length&&!this.destroyed)setTimeout(()=>void this.next(),this.options.gapMs??500)}}function ee(e){return typeof requestAnimationFrame==="function"?requestAnimationFrame(e):setTimeout(e,30)}function ve(e){if(typeof cancelAnimationFrame==="function")cancelAnimationFrame(e);else clearTimeout(e)}function we(e){let t=e.data;if(!t.id)return null;return{id:t.id,text:t.text,voice:t.voice,source:t.source,...t.eventId?{eventId:t.eventId}:{},...t.textTruncated===!0?{textTruncated:!0}:{},replay:e.source==="replay"}}function te(e){return Number.isFinite(e)?Math.min(1,Math.max(0,e)):1}class V{pixl;subs;hypeTrain;poll;prediction;goal;charity;adBreak;values={subs:new c(null),hypeTrain:new c(null),poll:new c(null),prediction:new c(null),goal:new c(null),charity:new c(null),adBreak:new c(null)};adBreakTimer=null;constructor(e){this.pixl=e;let t=this.values;this.subs=t.subs,this.hypeTrain=t.hypeTrain,this.poll=t.poll,this.prediction=t.prediction,this.goal=t.goal,this.charity=t.charity,this.adBreak=t.adBreak,e._onConnect(()=>this.load()),e._listen("subs.updated",(i)=>t.subs.set(i.data)),e._listen("twitch.hype_train_started",(i)=>t.hypeTrain.set(i.data)),e._listen("twitch.hype_train_progress",(i)=>t.hypeTrain.set(i.data)),e._listen("twitch.hype_train_ended",()=>t.hypeTrain.set(null)),e._listen("twitch.poll_started",(i)=>t.poll.set({...i.data,status:"active"})),e._listen("twitch.poll_progress",(i)=>t.poll.set({...i.data,status:"active"})),e._listen("twitch.poll_ended",(i)=>t.poll.set(i.data)),e._listen("twitch.prediction_started",(i)=>t.prediction.set({...i.data,status:"active",winner:null})),e._listen("twitch.prediction_progress",(i)=>t.prediction.set({...i.data,status:"active",winner:null})),e._listen("twitch.prediction_locked",(i)=>t.prediction.set({...i.data,status:"locked",winner:null})),e._listen("twitch.prediction_ended",(i)=>t.prediction.set(i.data)),e._listen("twitch.goal_started",(i)=>t.goal.set({...i.data,active:!0,achieved:!1})),e._listen("twitch.goal_progress",(i)=>{let n=t.goal.value;t.goal.set({type:n?.type??"",description:n?.description??"",...i.data,active:!0,achieved:!1})}),e._listen("twitch.goal_ended",(i)=>t.goal.set({...i.data,active:!1})),e._listen("twitch.charity_started",(i)=>t.charity.set({...i.data,current:{value:0,currency:i.data.target.currency},active:!0})),e._listen("twitch.charity_progress",(i)=>t.charity.set({charity:t.charity.value?.charity??"",...i.data,active:!0})),e._listen("twitch.charity_stopped",(i)=>t.charity.set({...i.data,active:!1})),e._listen("twitch.ad_break",(i)=>{let n=i.data.startedAt??i.at,r=new Date(Date.parse(n)+i.data.durationSec*1000);if(t.adBreak.set({durationSec:i.data.durationSec,startedAt:n,endsAt:r.toISOString()}),this.adBreakTimer)clearTimeout(this.adBreakTimer);this.adBreakTimer=setTimeout(()=>t.adBreak.set(null),Math.max(0,r.getTime()-Date.now()))})}async fetchSubs(e={}){let t=await this.pixl.request("/subs",{signal:e.signal});return this.values.subs.set(t),t}async load(){if(this.pixl.demo){this.values.subs.set(w["subs.updated"]);return}if(this.pixl.scopes?.includes("subs:read"))await this.fetchSubs().catch(()=>{})}_close(){if(this.adBreakTimer)clearTimeout(this.adBreakTimer);for(let e of Object.values(this.values))e.clear()}}function be(e){return`--pixl-${e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/_/g,"-").toLowerCase()}`}function ne(e){return`"${e.replace(/[\\"]/g,"\\$&").replace(/\n/g,"\\a ")}"`}function re(e){if(e.type==="list"||e.type==="trigger"||e.type==="voice")return!1;return!(e.type==="string"&&e.sensitive)}function xe(e,t,i){if(!re(e)||t===void 0||t===null)return null;switch(e.type){case"number":return typeof t==="number"&&Number.isFinite(t)?String(t):null;case"boolean":return t===!0?"1":"0";case"string":return typeof t==="string"?ne(t):null;case"font":return typeof t==="string"&&t?x(t):null;case"file":return typeof t==="string"&&t?`url(${ne(i(t))})`:null;case"color":case"enum":return typeof t==="string"&&t?t:null}}function Se(e){let t=new Map,i=(n,r)=>{for(let[s,o]of Object.entries(n??{}))if(!t.has(s)&&re(o))t.set(s,{field:o,value:r[s]})};return i(e.manifest?.controls,e.values),i(e.manifest?.settings,e.settings),t}class D{doc;fileUrl;vars=new Set;constructor(e,t){this.doc=e;this.fileUrl=t}apply(e){let t=this.doc.documentElement.style,i=new Set;for(let[n,{field:r,value:s}]of Se(e)){let o=xe(r,s,this.fileUrl);if(o===null)continue;let a=be(n);if(i.add(a),t.getPropertyValue(a)!==o)t.setProperty(a,o)}for(let n of this.vars)if(!i.has(n))t.removeProperty(n);this.vars=i}}class k{pixl;state;stateValue;fieldValues=new Map;actionHandlers=new Map;listHandlers=new Map;fields;values;settings;files={list:()=>this.pixl.request("/widget/files"),upload:(e,t)=>this.pixl.request("/widget/files",{method:"POST",query:{name:t??(e instanceof File?e.name:"datei")},raw:e}).then((i)=>({...i,url:this.fileUrl(i.path)}))};voicesCache=null;voices(){let e=Date.now();if(!this.voicesCache||e-this.voicesCache.at>30000){let t=this.pixl.request("/widget/voices");this.voicesCache={at:e,promise:t},t.catch(()=>{if(this.voicesCache?.promise===t)this.voicesCache=null})}return this.voicesCache.promise}preview;bindings;constructor(e,t,i={}){this.pixl=e;if(this.preview=i.preview===!0,this.bindings=i.bind&&typeof document<"u"?new D(document,(n)=>this.fileUrl(n)):null,t)this.bindings?.apply(t);this.stateValue=new c(t),this.state=this.stateValue,se(t),this.values=this.fieldProxy("controls"),this.settings=this.fieldProxy("settings"),this.fields=U(()=>this.stateValue.value)}fileUrl(e){if(!e.startsWith("/files/"))return e;let t=globalThis.__PIXL__?.widgetOrigin;return t?`${t.replace(/\/+$/,"")}${e}`:e}get manifest(){return this.stateValue.value?.manifest??null}get(e){let t=this.stateValue.value;return t&&e in t.values?t.values[e]:t?.settings[e]}on(e,t){let i=this.actionHandlers.get(e);if(!i)this.actionHandlers.set(e,i=new Set);return i.add(t),()=>{i.delete(t)}}onList(e,t){let i=this.listHandlers.get(e);if(!i)this.listHandlers.set(e,i=new Set);return i.add(t),()=>{i.delete(t)}}set(e,t){return this.batch([{name:e,set:t}])}add(e,t){return this.batch([{name:e,add:t}])}toggle(e){return this.batch([{name:e,toggle:!0}])}press(e){return this.batch([{name:e,press:!0}])}list(e){return{insert:(t,i)=>this.batch([{name:e,insert:t,...i===void 0?{}:{at:i}}]),update:(t,i)=>this.batch([{name:e,update:t,patch:i}]),remove:(t)=>this.batch([{name:e,remove:t}]),move:(t,i)=>this.batch([{name:e,move:t,to:i}]),replace:(t)=>this.batch([{name:e,set:t}])}}async batch(e){if(this.preview)return this.previewBatch(e);let t=await this.pixl.request("/widget",{method:"POST",body:{operations:e}});if(this.pixl.status.value!=="connected")this.applyState(t.state,t.changes)}async previewBatch(e){let t=this.stateValue.value;if(!t)return;let i=await this.pixl.request("/widget/preview",{method:"POST",body:{operations:e,settings:t.settings,values:t.values}}),n=this.stateValue.value??t,r={...n,values:{...n.values},settings:{...n.settings}};for(let s of i.changes)ae(r,s);if(i.changes.length)this.applyState(r,i.changes);for(let s of i.actions)for(let o of[...this.actionHandlers.get(s)??[]])u(o)}fromServer(e){let t=this.stateValue.value;if(!this.preview||!t)return e;return{...e,version:t.version,manifest:t.manifest,settings:oe(t,e,"settings"),values:oe(t,e,"controls")}}async _refetch(){this.applyState(this.fromServer(await this.pixl.request("/widget")),null)}_setState(e){this.applyState(this.fromServer(e),null)}_handle(e){if(e.type==="widget.action"){for(let n of[...this.actionHandlers.get(e.name)??[]])u(n);return}if(e.type==="widget.version")return;if(e.refetch){this._refetch().catch((n)=>console.warn("[pixl] Widget-Stand nicht geladen:",n));return}let t=this.stateValue.value;if(!t)return;let i={...t,values:{...t.values},settings:{...t.settings}};for(let n of e.changes)ae(i,n);this.applyState(i,e.changes)}applyState(e,t){let i=this.stateValue.value;se(e),this.stateValue.set(e),this.bindings?.apply(e);for(let[r,s]of this.fieldValues){let[o,a]=_e(r);s.set((o==="settings"?e.settings:e.values)[a])}let n=new Set;for(let r of t??[]){let s=this.listHandlers.get(r.name);if(!s?.size)continue;let o=Te(i,r),a=Pe(r,o);if(!a)continue;n.add(r.name);for(let d of[...s])u(()=>d(a))}for(let[r,s]of this.listHandlers){if(n.has(r))continue;let o=e.values[r]??e.settings[r],a=i?i.values[r]??i.settings[r]:void 0;if(!Array.isArray(o)||a===o||JSON.stringify(a)===JSON.stringify(o))continue;for(let d of[...s])u(()=>d({type:"replace",items:o}))}}fieldProxy(e){return new Proxy({},{get:(t,i)=>{if(typeof i!=="string")return;let n=`${e}:${i}`,r=this.fieldValues.get(n);if(!r){let s=this.stateValue.value;r=new c(s?(e==="settings"?s.settings:s.values)[i]:void 0),this.fieldValues.set(n,r)}return r},ownKeys:()=>{let t=this.stateValue.value;return Object.keys(t?e==="settings"?t.settings:t.values:{})},getOwnPropertyDescriptor:()=>({enumerable:!0,configurable:!0})})}}function U(e){let t=()=>{let n=e();return n?[...new Set([...Object.keys(n.values),...Object.keys(n.settings)])]:[]},i=(n)=>{let r=e();return r&&n in r.values?r.values[n]:r?.settings[n]};return new Proxy({},{get:(n,r)=>typeof r==="string"?i(r):void 0,has:(n,r)=>typeof r==="string"&&t().includes(r),ownKeys:()=>t(),getOwnPropertyDescriptor:(n,r)=>typeof r==="string"&&t().includes(r)?{value:i(r),writable:!1,enumerable:!0,configurable:!0}:void 0,set:(n,r)=>{throw TypeError(`[pixl] fields ist nur zum Lesen, ändern geht mit widget.set("${String(r)}", …)`)},deleteProperty:()=>!1})}function se(e){for(let t of J(e))P(t)}function oe(e,t,i){let n=(i==="settings"?e.manifest?.settings:e.manifest?.controls)??{},r=(i==="settings"?t.manifest?.settings:t.manifest?.controls)??{},s={...E(e,i)},o=E(t,i);for(let[a,d]of Object.entries(n))if(r[a]?.type===d.type&&a in o)s[a]=o[a];return s}function _e(e){let t=e.indexOf(":");return[e.slice(0,t),e.slice(t+1)]}function E(e,t){return t==="settings"?e.settings:e.values}function Te(e,t){if(!e)return[];let i=E(e,t.scope)[t.name];return Array.isArray(i)?i:[]}function ae(e,t){let i=E(e,t.scope);if("value"in t){i[t.name]=t.value;return}let n=Array.isArray(i[t.name])?[...i[t.name]]:[];if("inserted"in t){if(!n.some((r)=>r.id===t.inserted.id))n.splice(t.at,0,t.inserted)}else if("updated"in t){let r=n.findIndex((s)=>s.id===t.updated.id);if(r>=0)n[r]=t.updated}else if("removed"in t){let r=n.findIndex((s)=>s.id===t.removed);if(r>=0)n.splice(r,1)}else if("moved"in t){let r=n.findIndex((s)=>s.id===t.moved);if(r>=0){let[s]=n.splice(r,1);n.splice(Math.min(t.to,n.length),0,s)}}i[t.name]=n}function Pe(e,t){if("inserted"in e)return{type:"insert",item:e.inserted,at:e.at};if("updated"in e)return{type:"update",item:e.updated,previous:t.find((i)=>i.id===e.updated.id)??null};if("removed"in e)return{type:"remove",id:e.removed,previous:t.find((i)=>i.id===e.removed)??null};if("moved"in e)return{type:"move",id:e.moved,to:e.to};if(Array.isArray(e.value))return{type:"replace",items:e.value};return null}var Ee="https://pixl.lennox.zip",ke={id:"demo",name:"Demo-Kanal",login:"pixl",avatar:null},Ce=["events:read","tts:read","tts:write","subs:read","spotify:read","streamelements:read"];function B(){let e=globalThis.__PIXL__;if(e)return e;let t=typeof location<"u"?location.hash.slice(1):"";if(!t.includes("pixl-key="))return;let i=new URLSearchParams(t);return{key:i.get("pixl-key")??void 0,baseUrl:i.get("pixl-server")??void 0}}class g{static defaultBaseUrl;static sharedInstance=null;static shared(e){return g.sharedInstance??=new g(e)}static loadFont=P;static fontFamily=x;baseUrl;demo;status;channel;ready;events;twitch;spotify;tts;chat;streamElements;fields=U(()=>this.widgetInstance?.state.value??null);key;logs;widgetInstance=null;control;previewMode;fetchImpl;stream;statusValue=new c("closed");channelValue=new c(null);handlers=new Map;internal=new Map;connectHandlers=new Set;scopeList=null;warnedScopes=new Set;warnedNoWidget=!1;resolveReady;rejectReady;readySettled=!1;constructor(e={}){let t=B();if(this.demo=e.demo===!0,this.key=e.key??t?.key??"",this.control=!this.key&&t?.control===!0,this.previewMode=this.control&&t?.preview===!0,!this.key&&!this.demo&&!this.control)throw new l("unauthorized","pixl: `key` fehlt (API-Schlüssel aus „API & SDK“) oder `demo: true` setzen");if(t?.widget)this.widgetInstance=new k(this,t.widget,this.widgetOptions());if(this.logs=e.logs!==!1,g.sharedInstance??=this,this.fetchImpl=e.fetch??((...i)=>fetch(...i)),this.baseUrl=(e.baseUrl??t?.baseUrl??g.defaultBaseUrl??Ee).replace(/\/+$/,""),this.status=this.statusValue,this.channel=this.channelValue,this.ready=new Promise((i,n)=>{this.resolveReady=i,this.rejectReady=n}),this.ready.catch(()=>{}),this.events=new M(this),this.twitch=new V(this),this.spotify=new A(this),this.tts=new O(this),this.chat=new I(this),this.streamElements=new F(this),this.stream=this.demo?null:new R({url:`${this.baseUrl}/api/v1/stream?v=2`,token:()=>this.key,fetch:this.fetchImpl,onMessage:(i)=>this.handleMessage(i),onStatus:(i)=>this.handleStatus(i),log:(i)=>this.log(i)}),this.control){this.listenToDashboard(e.autoConnect!==!1);return}if(e.autoConnect!==!1)queueMicrotask(()=>this.connect())}widget(){if(!this.widgetInstance&&!this.warnedNoWidget&&this.scopeList)this.warnedNoWidget=!0,console.warn("[pixl] widget() geht nur in gehosteten Widgets, nicht mit einem normalen API-Schlüssel");return this.widgetInstance}get scopes(){return this.scopeList}on(e,t){let i=this.handlers.get(e);if(!i)this.handlers.set(e,i=new Set);return i.add(t),this.checkScope(e),()=>{i.delete(t)}}sendTestEvent(e,t={}){let i=w[e],n={type:e,data:{...i,...t},at:new Date().toISOString(),source:"test"};return this.dispatch(n),n}connect(){if(this.demo){this.handleConnected({type:"connected",protocol:2,channel:ke,scopes:Ce});return}this.stream?.start()}close(){if(g.sharedInstance===this)g.sharedInstance=null;if(this.stream?.stop(),this.demo)this.statusValue.set("closed");this.handlers.clear(),this.internal.clear(),this.connectHandlers.clear(),this.twitch._close(),this.spotify._close(),this.channelValue.clear(),this.statusValue.clear()}async request(e,t={}){return await(await this.fetchRaw(e,t)).json()}async fetchRaw(e,t={}){if(this.demo)throw new l("demo","Im Demo-Modus gibt es keinen Server");if(!this.key)throw new l("unauthorized","Noch kein Zugang (Steuer-Seite wartet auf das Dashboard)");let i=new URL(`${this.baseUrl}/api/v1${e}`);for(let[s,o]of Object.entries(t.query??{}))if(o!==void 0&&o!=="")i.searchParams.set(s,String(o));let n={Authorization:`Bearer ${this.key}`};if(t.body!==void 0)n["Content-Type"]="application/json";else if(t.raw)n["Content-Type"]=t.raw.type||"application/octet-stream";let r;try{r=await this.fetchImpl(i,{method:t.method??"GET",headers:n,body:t.raw??(t.body!==void 0?JSON.stringify(t.body):void 0),signal:t.signal})}catch(s){if(s instanceof DOMException&&s.name==="AbortError")throw s;throw new l("network","pixl ist gerade nicht erreichbar")}if(!r.ok)throw await z(r);return r}_onConnect(e){this.connectHandlers.add(e)}_listen(e,t){let i=this.internal.get(e);if(!i)this.internal.set(e,i=new Set);return i.add(t),()=>{i.delete(t)}}widgetOptions(){let e=B()?.preview===!0;return{preview:e,bind:!this.control||e}}get _preview(){return this.previewMode}_log(e){this.log(e)}handleMessage(e){if(e.type==="connected"){this.handleConnected(e);return}if(e.type==="access_revoked")return;if(e.type.startsWith("widget.")){this.handleWidgetMessage(e);return}this.dispatch(e)}handleWidgetMessage(e){if(e.type==="widget.version"){if(!this.control&&B()?.widget&&typeof location<"u")location.reload();return}this.widgetInstance?._handle(e)}listenToDashboard(e){if(typeof window>"u"||window.parent===window){this.log("Steuer-Seite außerhalb des Dashboards geöffnet, kein Zugang");return}window.addEventListener("message",(t)=>{if(t.source!==window.parent||typeof t.data!=="object"||!t.data)return;if(t.data.type==="pixl:control"&&typeof t.data.token==="string"){let i=!this.key;if(this.key=t.data.token,i&&e)this.connect()}}),Q(),window.parent.postMessage({type:"pixl:ready"},"*")}handleConnected(e){if(this.scopeList=e.scopes,this.channelValue.set(e.channel),e.widget)if(this.widgetInstance)this.widgetInstance._setState(e.widget);else this.widgetInstance=new k(this,e.widget,this.widgetOptions());this.statusValue.set("connected");for(let i of this.handlers.keys())this.checkScope(i);let t=[...this.connectHandlers].map((i)=>Promise.resolve().then(i));Promise.allSettled(t).then(()=>{if(this.readySettled)return;this.readySettled=!0,this.resolveReady()})}handleStatus(e){if(e==="connected")return;if(this.statusValue.set(e),e==="unauthorized"&&!this.readySettled)this.readySettled=!0,this.rejectReady(new l("unauthorized","Zugang ungültig oder widerrufen"))}dispatch(e){for(let t of[...this.internal.get(e.type)??[]])u(()=>t(e));for(let t of[...this.handlers.get(e.type)??[]])u(()=>t(e));for(let t of[...this.handlers.get("*")??[]])u(()=>t(e))}checkScope(e){if(!this.scopeList||e==="*")return;let t=H(e);if(!t||this.scopeList.includes(t)||this.warnedScopes.has(t))return;this.warnedScopes.add(t),console.warn(`[pixl] "${e}" kommt nie an: dem Schlüssel fehlt die Erlaubnis ${t}`)}log(e){if(this.logs)console.info(`[pixl] ${e}`)}}function le(e){if(!e)return;try{let t=new URL(e);return t.pathname.replace(/\/+$/,"").endsWith("/sdk/pixl.js")?t.origin:void 0}catch{return}}var We=typeof document<"u"?document.currentScript:null,de=le(We?.src);if(de)g.defaultBaseUrl=de;Object.assign(globalThis,{Pixl:g,PixlError:l});var Ie=globalThis.__PIXL__;if(Ie?.widget&&typeof document<"u"){let e=()=>void g.shared();if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",e,{once:!0});else queueMicrotask(e)}})();
@@ -0,0 +1,22 @@
1
+ import { type SpotifyNowPlaying } from "./api-types/index.js";
2
+ import type { Pixl } from "./client.js";
3
+ import { type LiveValue } from "./live-value.js";
4
+ export type { SpotifyNowPlaying };
5
+ export declare class PixlSpotify {
6
+ private readonly pixl;
7
+ /**
8
+ * Was gerade läuft (Scope `spotify:read`, Modul Musik). `track` ist null, wenn nichts
9
+ * läuft. Der Wert selbst null, solange unbekannt oder das Modul aus ist.
10
+ */
11
+ readonly nowPlaying: LiveValue<SpotifyNowPlaying | null>;
12
+ private readonly value;
13
+ constructor(pixl: Pixl);
14
+ /**
15
+ * Fortschritt des laufenden Songs in ms zum Zeitpunkt `now`. Rechnet ab der letzten
16
+ * Messung weiter, solange der Song läuft, höchstens bis zur Songlänge.
17
+ */
18
+ progress(now?: number): number;
19
+ private load;
20
+ /** @internal */
21
+ _close(): void;
22
+ }
@@ -0,0 +1,30 @@
1
+ import type { StreamMessage } from "./api-types/index.js";
2
+ export type ConnectionStatus = "connecting" | "connected" | "disconnected" | "unauthorized" | "closed";
3
+ export type StreamOptions = {
4
+ url: string;
5
+ /** Zugang, wird bei jedem Verbinden neu gelesen (Steuer-Tokens laufen ab und werden erneuert) */
6
+ token: () => string;
7
+ onMessage: (message: StreamMessage) => void;
8
+ onStatus: (status: ConnectionStatus) => void;
9
+ log: (message: string) => void;
10
+ /** Zum Testen austauschbar */
11
+ fetch?: typeof fetch;
12
+ };
13
+ export declare class LiveStream {
14
+ private readonly options;
15
+ private abort;
16
+ private reconnectTimer;
17
+ private watchdog;
18
+ private attempt;
19
+ private stopped;
20
+ private everConnected;
21
+ private lostAt;
22
+ constructor(options: StreamOptions);
23
+ start(): void;
24
+ stop(): void;
25
+ private run;
26
+ private scheduleReconnect;
27
+ private armWatchdog;
28
+ private clearWatchdog;
29
+ private read;
30
+ }