@juspay/svelte-ui-components 2.130.0 → 2.131.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/TaskList/TaskList.svelte +268 -0
- package/dist/TaskList/TaskList.svelte.d.ts +4 -0
- package/dist/TaskList/properties.d.ts +24 -0
- package/dist/TaskList/properties.js +1 -0
- package/dist/ThinkingIndicator/ThinkingIndicator.svelte +621 -35
- package/dist/ThinkingIndicator/ThinkingIndicator.svelte.d.ts +1 -1
- package/dist/ThinkingIndicator/properties.d.ts +63 -3
- package/dist/ToolCallLog/ToolCallLog.svelte +418 -0
- package/dist/ToolCallLog/ToolCallLog.svelte.d.ts +4 -0
- package/dist/ToolCallLog/properties.d.ts +27 -0
- package/dist/ToolCallLog/properties.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/soundKit/properties.d.ts +29 -0
- package/dist/soundKit/properties.js +1 -0
- package/dist/soundKit/soundKit.d.ts +2 -0
- package/dist/soundKit/soundKit.js +284 -0
- package/package.json +1 -1
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
const DEFAULT_STORAGE_KEY = 'sui-sound-enabled';
|
|
2
|
+
const DEFAULT_MASTER_GAIN = 0.32;
|
|
3
|
+
const SOUND_ATTRIBUTE = 'data-sound';
|
|
4
|
+
const TICK_SELECTOR = 'input[type="checkbox"], input[type="radio"], [role="switch"], [role="checkbox"], [role="tab"]';
|
|
5
|
+
const PAGE_SELECTOR = 'a';
|
|
6
|
+
const PRESS_SELECTOR = 'button, [role="button"], summary';
|
|
7
|
+
const createNoiseBuffer = (context, durationSeconds) => {
|
|
8
|
+
const frameCount = Math.max(1, Math.floor(context.sampleRate * durationSeconds));
|
|
9
|
+
const buffer = context.createBuffer(1, frameCount, context.sampleRate);
|
|
10
|
+
const channel = buffer.getChannelData(0);
|
|
11
|
+
for (let sampleIndex = 0; sampleIndex < frameCount; sampleIndex += 1) {
|
|
12
|
+
channel[sampleIndex] = Math.random() * 2 - 1;
|
|
13
|
+
}
|
|
14
|
+
return buffer;
|
|
15
|
+
};
|
|
16
|
+
const connectChain = (nodes) => {
|
|
17
|
+
for (let nodeIndex = 0; nodeIndex < nodes.length - 1; nodeIndex += 1) {
|
|
18
|
+
nodes[nodeIndex].connect(nodes[nodeIndex + 1]);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
/** A light key tap: a highpassed noise crack layered under a short 680Hz sine body. */
|
|
22
|
+
const playPress = (context, master) => {
|
|
23
|
+
const now = context.currentTime;
|
|
24
|
+
const crackDuration = 0.02;
|
|
25
|
+
const crackSource = context.createBufferSource();
|
|
26
|
+
crackSource.buffer = createNoiseBuffer(context, crackDuration);
|
|
27
|
+
const crackFilter = context.createBiquadFilter();
|
|
28
|
+
crackFilter.type = 'highpass';
|
|
29
|
+
crackFilter.frequency.value = 2400;
|
|
30
|
+
const crackGain = context.createGain();
|
|
31
|
+
crackGain.gain.setValueAtTime(0.3, now);
|
|
32
|
+
crackGain.gain.exponentialRampToValueAtTime(0.0001, now + 0.009);
|
|
33
|
+
connectChain([crackSource, crackFilter, crackGain, master]);
|
|
34
|
+
crackSource.start(now);
|
|
35
|
+
crackSource.stop(now + crackDuration);
|
|
36
|
+
const bodyDuration = 0.03;
|
|
37
|
+
const body = context.createOscillator();
|
|
38
|
+
body.type = 'sine';
|
|
39
|
+
body.frequency.value = 680;
|
|
40
|
+
const bodyGain = context.createGain();
|
|
41
|
+
bodyGain.gain.setValueAtTime(0.0001, now);
|
|
42
|
+
bodyGain.gain.exponentialRampToValueAtTime(0.6, now + 0.008);
|
|
43
|
+
bodyGain.gain.exponentialRampToValueAtTime(0.0001, now + bodyDuration);
|
|
44
|
+
connectChain([body, bodyGain, master]);
|
|
45
|
+
body.start(now);
|
|
46
|
+
body.stop(now + bodyDuration);
|
|
47
|
+
};
|
|
48
|
+
/** A tiny bandpassed square blip for checkbox/switch/radio/tab semantics. */
|
|
49
|
+
const playTick = (context, master) => {
|
|
50
|
+
const now = context.currentTime;
|
|
51
|
+
const duration = 0.012;
|
|
52
|
+
const oscillator = context.createOscillator();
|
|
53
|
+
oscillator.type = 'square';
|
|
54
|
+
oscillator.frequency.value = 2100;
|
|
55
|
+
const bandpass = context.createBiquadFilter();
|
|
56
|
+
bandpass.type = 'bandpass';
|
|
57
|
+
bandpass.frequency.value = 2600;
|
|
58
|
+
bandpass.Q.value = 1.6;
|
|
59
|
+
const gain = context.createGain();
|
|
60
|
+
gain.gain.setValueAtTime(0.25, now);
|
|
61
|
+
gain.gain.exponentialRampToValueAtTime(0.0001, now + duration);
|
|
62
|
+
connectChain([oscillator, bandpass, gain, master]);
|
|
63
|
+
oscillator.start(now);
|
|
64
|
+
oscillator.stop(now + duration);
|
|
65
|
+
};
|
|
66
|
+
/** A soft lowpassed noise puff — the un-press. */
|
|
67
|
+
const playRelease = (context, master) => {
|
|
68
|
+
const now = context.currentTime;
|
|
69
|
+
const duration = 0.03;
|
|
70
|
+
const source = context.createBufferSource();
|
|
71
|
+
source.buffer = createNoiseBuffer(context, duration);
|
|
72
|
+
const lowpass = context.createBiquadFilter();
|
|
73
|
+
lowpass.type = 'lowpass';
|
|
74
|
+
lowpass.frequency.value = 1600;
|
|
75
|
+
const gain = context.createGain();
|
|
76
|
+
gain.gain.setValueAtTime(0.18, now);
|
|
77
|
+
gain.gain.exponentialRampToValueAtTime(0.0001, now + duration);
|
|
78
|
+
connectChain([source, lowpass, gain, master]);
|
|
79
|
+
source.start(now);
|
|
80
|
+
source.stop(now + duration);
|
|
81
|
+
};
|
|
82
|
+
/** A gentle upward sine sweep for navigation/links. */
|
|
83
|
+
const playPage = (context, master) => {
|
|
84
|
+
const now = context.currentTime;
|
|
85
|
+
const duration = 0.12;
|
|
86
|
+
const oscillator = context.createOscillator();
|
|
87
|
+
oscillator.type = 'sine';
|
|
88
|
+
oscillator.frequency.setValueAtTime(430, now);
|
|
89
|
+
oscillator.frequency.linearRampToValueAtTime(640, now + duration);
|
|
90
|
+
const gain = context.createGain();
|
|
91
|
+
gain.gain.setValueAtTime(0.0001, now);
|
|
92
|
+
gain.gain.exponentialRampToValueAtTime(0.22, now + 0.02);
|
|
93
|
+
gain.gain.exponentialRampToValueAtTime(0.0001, now + duration);
|
|
94
|
+
connectChain([oscillator, gain, master]);
|
|
95
|
+
oscillator.start(now);
|
|
96
|
+
oscillator.stop(now + duration);
|
|
97
|
+
};
|
|
98
|
+
/** A rounded lowpassed sine thump for status/heartbeat pulses. */
|
|
99
|
+
const playPulse = (context, master) => {
|
|
100
|
+
const now = context.currentTime;
|
|
101
|
+
const duration = 0.08;
|
|
102
|
+
const oscillator = context.createOscillator();
|
|
103
|
+
oscillator.type = 'sine';
|
|
104
|
+
oscillator.frequency.value = 330;
|
|
105
|
+
const lowpass = context.createBiquadFilter();
|
|
106
|
+
lowpass.type = 'lowpass';
|
|
107
|
+
lowpass.frequency.value = 2200;
|
|
108
|
+
const gain = context.createGain();
|
|
109
|
+
gain.gain.setValueAtTime(0.0001, now);
|
|
110
|
+
gain.gain.exponentialRampToValueAtTime(0.3, now + 0.015);
|
|
111
|
+
gain.gain.exponentialRampToValueAtTime(0.0001, now + duration);
|
|
112
|
+
connectChain([oscillator, lowpass, gain, master]);
|
|
113
|
+
oscillator.start(now);
|
|
114
|
+
oscillator.stop(now + duration);
|
|
115
|
+
};
|
|
116
|
+
const recipes = {
|
|
117
|
+
press: playPress,
|
|
118
|
+
tick: playTick,
|
|
119
|
+
release: playRelease,
|
|
120
|
+
page: playPage,
|
|
121
|
+
pulse: playPulse
|
|
122
|
+
};
|
|
123
|
+
/** Narrows a string to a `SoundName` via exhaustive literal matching — no assertion needed. */
|
|
124
|
+
const resolveSoundName = (value) => {
|
|
125
|
+
switch (value) {
|
|
126
|
+
case 'press':
|
|
127
|
+
case 'tick':
|
|
128
|
+
case 'release':
|
|
129
|
+
case 'page':
|
|
130
|
+
case 'pulse':
|
|
131
|
+
return value;
|
|
132
|
+
default:
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
/** Same as `resolveSoundName` but also recognizes the explicit-silence `'off'` override. */
|
|
137
|
+
const resolveOverride = (value) => {
|
|
138
|
+
if (value === 'off') {
|
|
139
|
+
return 'off';
|
|
140
|
+
}
|
|
141
|
+
return resolveSoundName(value);
|
|
142
|
+
};
|
|
143
|
+
const readStoredEnabled = (storageKey) => {
|
|
144
|
+
if (typeof window === 'undefined') {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
return window.localStorage.getItem(storageKey) === 'true';
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
const writeStoredEnabled = (storageKey, enabled) => {
|
|
155
|
+
if (typeof window === 'undefined') {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
window.localStorage.setItem(storageKey, enabled ? 'true' : 'false');
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Storage can be unavailable (private mode, quota) — enabled state just won't persist.
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
export const createSoundKit = (options = {}) => {
|
|
166
|
+
const storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
167
|
+
const masterGain = options.masterGain ?? DEFAULT_MASTER_GAIN;
|
|
168
|
+
// Enabled state starts unresolved so no storage read happens at createSoundKit() time —
|
|
169
|
+
// resolveEnabled() reads it lazily, on first play/isEnabled/toggle call.
|
|
170
|
+
let enabled = null;
|
|
171
|
+
let engine = null;
|
|
172
|
+
let attachedRoot = null;
|
|
173
|
+
const resolveEnabled = () => {
|
|
174
|
+
if (enabled === null) {
|
|
175
|
+
enabled = readStoredEnabled(storageKey);
|
|
176
|
+
}
|
|
177
|
+
return enabled;
|
|
178
|
+
};
|
|
179
|
+
const ensureEngine = () => {
|
|
180
|
+
if (typeof window === 'undefined') {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
if (engine) {
|
|
184
|
+
return engine;
|
|
185
|
+
}
|
|
186
|
+
const AudioContextConstructor = window.AudioContext;
|
|
187
|
+
if (typeof AudioContextConstructor !== 'function') {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const context = new AudioContextConstructor();
|
|
191
|
+
const master = context.createGain();
|
|
192
|
+
master.gain.value = masterGain;
|
|
193
|
+
master.connect(context.destination);
|
|
194
|
+
engine = { context, master };
|
|
195
|
+
return engine;
|
|
196
|
+
};
|
|
197
|
+
const play = (name) => {
|
|
198
|
+
if (!resolveEnabled()) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const activeEngine = ensureEngine();
|
|
202
|
+
if (!activeEngine) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (activeEngine.context.state === 'suspended') {
|
|
206
|
+
activeEngine.context.resume().catch(() => {
|
|
207
|
+
// A rejected resume just means this gesture can't unlock audio; the sound skips.
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
recipes[name](activeEngine.context, activeEngine.master);
|
|
211
|
+
};
|
|
212
|
+
// Typed as the base `Event` (only `.target` is used) so it matches `EventListener` across
|
|
213
|
+
// the `Document | HTMLElement` union `attachClicks` accepts — narrowing to `MouseEvent`
|
|
214
|
+
// here would make TS fall back to the untyped `EventListenerOrEventListenerObject` overload.
|
|
215
|
+
const handleClick = (event) => {
|
|
216
|
+
if (!resolveEnabled()) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const target = event.target;
|
|
220
|
+
if (!(target instanceof Element)) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const overrideElement = target.closest(`[${SOUND_ATTRIBUTE}]`);
|
|
224
|
+
if (overrideElement !== null) {
|
|
225
|
+
const overrideValue = overrideElement.getAttribute(SOUND_ATTRIBUTE) ?? '';
|
|
226
|
+
const resolved = resolveOverride(overrideValue);
|
|
227
|
+
if (resolved !== null && resolved !== 'off') {
|
|
228
|
+
play(resolved);
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (target.closest(TICK_SELECTOR) !== null) {
|
|
233
|
+
play('tick');
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (target.closest(PAGE_SELECTOR) !== null) {
|
|
237
|
+
play('page');
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (target.closest(PRESS_SELECTOR) !== null) {
|
|
241
|
+
play('press');
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
const attachClicks = (root) => {
|
|
245
|
+
if (typeof document === 'undefined') {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const nextRoot = root ?? document;
|
|
249
|
+
if (attachedRoot) {
|
|
250
|
+
attachedRoot.removeEventListener('click', handleClick, true);
|
|
251
|
+
}
|
|
252
|
+
nextRoot.addEventListener('click', handleClick, true);
|
|
253
|
+
attachedRoot = nextRoot;
|
|
254
|
+
};
|
|
255
|
+
const detachClicks = () => {
|
|
256
|
+
if (!attachedRoot) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
attachedRoot.removeEventListener('click', handleClick, true);
|
|
260
|
+
attachedRoot = null;
|
|
261
|
+
};
|
|
262
|
+
const setEnabled = (nextEnabled) => {
|
|
263
|
+
enabled = nextEnabled;
|
|
264
|
+
writeStoredEnabled(storageKey, nextEnabled);
|
|
265
|
+
};
|
|
266
|
+
const isEnabled = () => {
|
|
267
|
+
return resolveEnabled();
|
|
268
|
+
};
|
|
269
|
+
const toggle = () => {
|
|
270
|
+
const nextEnabled = !resolveEnabled();
|
|
271
|
+
setEnabled(nextEnabled);
|
|
272
|
+
return nextEnabled;
|
|
273
|
+
};
|
|
274
|
+
const dispose = () => {
|
|
275
|
+
detachClicks();
|
|
276
|
+
if (engine) {
|
|
277
|
+
engine.context.close().catch(() => {
|
|
278
|
+
// Already-closed contexts reject close(); there is nothing left to clean up.
|
|
279
|
+
});
|
|
280
|
+
engine = null;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
return { play, attachClicks, detachClicks, setEnabled, isEnabled, toggle, dispose };
|
|
284
|
+
};
|