@xsynaptic/sonic-ui 0.23.0 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/default/elements/readout.js +3 -2
- package/dist/default/elements/sonic-element.js +23 -4
- package/dist/default/elements/value-element.js +2 -2
- package/dist/default/lib/spoken-duration.js +3 -0
- package/dist/default/lib/waveform-bands.js +37 -24
- package/dist/default/lib/waveform-scene.js +12 -1
- package/dist/dev/elements/readout.js +3 -2
- package/dist/dev/elements/readout.js.map +1 -1
- package/dist/dev/elements/sonic-element.js +23 -4
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/value-element.js +2 -2
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +3 -0
- package/dist/dev/lib/spoken-duration.js +3 -0
- package/dist/dev/lib/spoken-duration.js.map +1 -1
- package/dist/dev/lib/waveform-bands.js +37 -24
- package/dist/dev/lib/waveform-bands.js.map +1 -1
- package/dist/dev/lib/waveform-scene.d.ts +3 -0
- package/dist/dev/lib/waveform-scene.js +12 -1
- package/dist/dev/lib/waveform-scene.js.map +1 -1
- package/dist/styles/button.css +30 -11
- package/dist/styles/dial.css +13 -15
- package/dist/styles/envelope.css +2 -2
- package/dist/styles/led.css +28 -13
- package/dist/styles/material/cap.css +6 -6
- package/dist/styles/material/core.css +53 -1
- package/dist/styles/material/glass.css +11 -1
- package/dist/styles/material/keycap.css +3 -3
- package/dist/styles/material/pane.css +8 -6
- package/dist/styles/material/readout.css +19 -7
- package/dist/styles/material/scale.css +4 -4
- package/dist/styles/material/value.css +12 -3
- package/dist/styles/meter.css +6 -6
- package/dist/styles/panel.css +2 -3
- package/dist/styles/ring.css +3 -3
- package/dist/styles/segmented.css +6 -6
- package/dist/styles/slider.css +28 -29
- package/dist/styles/switch.css +49 -51
- package/dist/styles/toggle.css +31 -37
- package/dist/styles/waveform.css +23 -2
- package/dist/styles/wavestrip.css +4 -4
- package/dist/styles/xy.css +10 -11
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spoken-duration.js","names":[],"sources":["../../../src/lib/spoken-duration.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"spoken-duration.js","names":[],"sources":["../../../src/lib/spoken-duration.ts"],"sourcesContent":["import { formatClock } from '#lib/clock.ts';\n\nconst formatters = new Map<string, Intl.DurationFormat>();\n\nfunction formatterFor(language: string, isZero: boolean): Intl.DurationFormat {\n\tconst key = `${language}|${String(isZero)}`;\n\tconst cached = formatters.get(key);\n\tif (cached) return cached;\n\n\t// All-zero units format to an empty string unless the seconds are forced\n\tconst options = { secondsDisplay: isZero ? 'always' : 'auto', style: 'long' } as const;\n\tlet formatter: Intl.DurationFormat;\n\n\ttry {\n\t\tformatter = new Intl.DurationFormat(language || undefined, options);\n\t} catch {\n\t\tformatter = new Intl.DurationFormat(undefined, options);\n\t}\n\tformatters.set(key, formatter);\n\n\treturn formatter;\n}\n\nexport function spokenDuration(seconds: number, language = ''): string {\n\tif (!('DurationFormat' in Intl)) return formatClock(seconds);\n\n\t// Truncated as the clock is, so every unit carries one sign; mixed signs throw\n\tconst total = Math.trunc(seconds);\n\n\treturn formatterFor(language, total === 0).format({\n\t\thours: Math.trunc(total / 3600),\n\t\tminutes: Math.trunc(total / 60) % 60,\n\t\tseconds: total % 60,\n\t});\n}\n"],"mappings":";;;AAEA,MAAM,6BAAa,IAAI,IAAiC;AAExD,SAAS,aAAa,UAAkB,QAAsC;CAC7E,MAAM,MAAM,GAAG,SAAS,GAAG,OAAO,MAAM;CACxC,MAAM,SAAS,WAAW,IAAI,GAAG;CACjC,IAAI,QAAQ,OAAO;CAGnB,MAAM,UAAU;EAAE,gBAAgB,SAAS,WAAW;EAAQ,OAAO;CAAO;CAC5E,IAAI;CAEJ,IAAI;EACH,YAAY,IAAI,KAAK,eAAe,YAAY,QAAW,OAAO;CACnE,QAAQ;EACP,YAAY,IAAI,KAAK,eAAe,QAAW,OAAO;CACvD;CACA,WAAW,IAAI,KAAK,SAAS;CAE7B,OAAO;AACR;AAEA,SAAgB,eAAe,SAAiB,WAAW,IAAY;CACtE,IAAI,EAAE,oBAAoB,OAAO,OAAO,YAAY,OAAO;CAG3D,MAAM,QAAQ,KAAK,MAAM,OAAO;CAEhC,OAAO,aAAa,UAAU,UAAU,CAAC,CAAC,CAAC,OAAO;EACjD,OAAO,KAAK,MAAM,QAAQ,IAAI;EAC9B,SAAS,KAAK,MAAM,QAAQ,EAAE,IAAI;EAClC,SAAS,QAAQ;CAClB,CAAC;AACF"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { clampProportion } from "./math.js";
|
|
2
|
+
|
|
1
3
|
//#region src/lib/waveform-bands.ts
|
|
2
4
|
function fullScaleOf({ fullScale, levels }) {
|
|
3
5
|
if (fullScale !== void 0) return fullScale;
|
|
@@ -32,33 +34,44 @@ function bandTable(bands) {
|
|
|
32
34
|
weightOf: (level) => weights[level] ?? 0
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
let
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
255
|
|
57
|
-
|
|
37
|
+
function groupMixer({ bandCount, levels, weightOf }, colours, { contrast, normalize, tilt }) {
|
|
38
|
+
const gains = Array.from({ length: bandCount }, (_gain, band) => 10 ** (tilt * band / 20));
|
|
39
|
+
const amount = clampProportion(normalize);
|
|
40
|
+
return (fromFrame, toFrame) => {
|
|
41
|
+
let red = 0;
|
|
42
|
+
let green = 0;
|
|
43
|
+
let blue = 0;
|
|
44
|
+
let total = 0;
|
|
45
|
+
for (let band = 0; band < bandCount; band += 1) {
|
|
46
|
+
const colour = colours[band];
|
|
47
|
+
if (!colour) continue;
|
|
48
|
+
let weight = 0;
|
|
49
|
+
for (let bandFrame = fromFrame; bandFrame < toFrame; bandFrame += 1) weight += weightOf(levels[bandFrame * bandCount + band] ?? 0);
|
|
50
|
+
weight *= gains[band] ?? 1;
|
|
51
|
+
if (contrast !== 1) weight **= contrast;
|
|
52
|
+
red += weight * colour[0];
|
|
53
|
+
green += weight * colour[1];
|
|
54
|
+
blue += weight * colour[2];
|
|
55
|
+
total += weight;
|
|
56
|
+
}
|
|
57
|
+
if (total === 0) return;
|
|
58
|
+
const scale = 255 / Math.max(red, green, blue, Number.MIN_VALUE) * amount + (1 - amount) / total;
|
|
59
|
+
return [
|
|
60
|
+
red * scale,
|
|
61
|
+
green * scale,
|
|
62
|
+
blue * scale,
|
|
63
|
+
255
|
|
64
|
+
];
|
|
65
|
+
};
|
|
58
66
|
}
|
|
59
67
|
function bandStrip(table, colours, view) {
|
|
60
68
|
if (view.pixelsPerSecond <= 0 || !colours.some(Boolean)) return void 0;
|
|
61
69
|
const { bandCount, framesPerSecond, levels } = table;
|
|
70
|
+
const mixGroup = groupMixer(table, colours, view.mix ?? {
|
|
71
|
+
contrast: 1,
|
|
72
|
+
normalize: 1,
|
|
73
|
+
tilt: 0
|
|
74
|
+
});
|
|
62
75
|
const pxPerFrame = view.pixelsPerSecond / framesPerSecond;
|
|
63
76
|
const frames = Math.max(1, Math.round(1 / pxPerFrame));
|
|
64
77
|
const groupWidth = frames * pxPerFrame;
|
|
@@ -70,7 +83,7 @@ function bandStrip(table, colours, view) {
|
|
|
70
83
|
const pixels = new Uint8ClampedArray(count * 4);
|
|
71
84
|
for (let index = 0; index < count; index += 1) {
|
|
72
85
|
const fromFrame = (first + index) * frames;
|
|
73
|
-
const mixed = mixGroup(
|
|
86
|
+
const mixed = mixGroup(fromFrame, fromFrame + frames);
|
|
74
87
|
if (mixed) pixels.set(mixed, index * 4);
|
|
75
88
|
}
|
|
76
89
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waveform-bands.js","names":[],"sources":["../../../src/lib/waveform-bands.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"waveform-bands.js","names":[],"sources":["../../../src/lib/waveform-bands.ts"],"sourcesContent":["import { clampProportion } from '#lib/math.ts';\n\nexport interface WaveformBands {\n\t/** Bands per frame, interleaved in `levels`, lowest first */\n\tbandCount: number;\n\tframesPerSecond: number;\n\t/** Unset follows the array: 255, 65535 or 1 */\n\tfullScale?: number;\n\tlevels: Float32Array | Uint8Array | Uint16Array;\n\t/** Reads a level as decibels, from this just above zero up to 0 at full scale; unset means linear */\n\tminDecibels?: number;\n}\n\nexport type BandChannels = readonly [number, number, number];\n\nexport interface BandTable {\n\tbandCount: number;\n\tframesPerSecond: number;\n\tlevels: WaveformBands['levels'];\n\tweightOf: (level: number) => number;\n}\n\nexport interface BandMix {\n\tcontrast: number;\n\tnormalize: number;\n\ttilt: number;\n}\n\ninterface BandView {\n\tmix?: BandMix;\n\tpixelsPerSecond: number;\n\tstartSeconds: number;\n\twidth: number;\n}\n\ninterface BandStrip {\n\tcount: number;\n\tgroupWidth: number;\n\tpixels: Uint8ClampedArray<ArrayBuffer>;\n\tx: number;\n}\n\nfunction fullScaleOf({ fullScale, levels }: WaveformBands): number {\n\tif (fullScale !== undefined) return fullScale;\n\tif (levels instanceof Uint8Array) return 255;\n\n\treturn levels instanceof Uint16Array ? 65_535 : 1;\n}\n\nfunction amplitudeReader(bands: WaveformBands): (level: number) => number {\n\tconst fullScale = fullScaleOf(bands);\n\tconst minDecibels = Math.min(0, bands.minDecibels ?? 0);\n\n\treturn (level) => {\n\t\tconst proportion = Math.min(1, level / fullScale);\n\t\tif (!(proportion > 0)) return 0;\n\n\t\treturn minDecibels < 0 ? 10 ** ((minDecibels * (1 - proportion)) / 20) : proportion;\n\t};\n}\n\nexport function bandTable(bands: undefined | WaveformBands): BandTable | undefined {\n\tif (!bands) return undefined;\n\n\tconst { bandCount, framesPerSecond, levels } = bands;\n\tif (!Number.isSafeInteger(bandCount) || bandCount < 1 || !(framesPerSecond > 0)) return undefined;\n\n\tconst amplitudeOf = amplitudeReader(bands);\n\tif (levels instanceof Float32Array) {\n\t\treturn { bandCount, framesPerSecond, levels, weightOf: amplitudeOf };\n\t}\n\n\tconst weights = Float32Array.from(\n\t\t{ length: levels instanceof Uint8Array ? 256 : 65_536 },\n\t\t(_weight, level) => amplitudeOf(level),\n\t);\n\n\treturn { bandCount, framesPerSecond, levels, weightOf: (level) => weights[level] ?? 0 };\n}\n\n// Loudness is the envelope's height, so the mix is hue: scaled to its strongest channel, or averaged to stay inside the palette\nfunction groupMixer(\n\t{ bandCount, levels, weightOf }: BandTable,\n\tcolours: ReadonlyArray<BandChannels | undefined>,\n\t{ contrast, normalize, tilt }: BandMix,\n): (fromFrame: number, toFrame: number) => [number, number, number, number] | undefined {\n\tconst gains = Array.from({ length: bandCount }, (_gain, band) => 10 ** ((tilt * band) / 20));\n\tconst amount = clampProportion(normalize);\n\n\treturn (fromFrame, toFrame) => {\n\t\tlet red = 0;\n\t\tlet green = 0;\n\t\tlet blue = 0;\n\t\tlet total = 0;\n\n\t\tfor (let band = 0; band < bandCount; band += 1) {\n\t\t\tconst colour = colours[band];\n\t\t\tif (!colour) continue;\n\n\t\t\tlet weight = 0;\n\n\t\t\tfor (let bandFrame = fromFrame; bandFrame < toFrame; bandFrame += 1) {\n\t\t\t\tweight += weightOf(levels[bandFrame * bandCount + band] ?? 0);\n\t\t\t}\n\t\t\tweight *= gains[band] ?? 1;\n\t\t\tif (contrast !== 1) weight **= contrast;\n\t\t\tred += weight * colour[0];\n\t\t\tgreen += weight * colour[1];\n\t\t\tblue += weight * colour[2];\n\t\t\ttotal += weight;\n\t\t}\n\t\tif (total === 0) return;\n\n\t\tconst full = 255 / Math.max(red, green, blue, Number.MIN_VALUE);\n\t\tconst scale = full * amount + (1 - amount) / total;\n\n\t\treturn [red * scale, green * scale, blue * scale, 255];\n\t};\n}\n\n// Keyed to absolute frames, as buckets are to pairs; keyed to the window, the colours shimmer as it scrolls\nexport function bandStrip(\n\ttable: BandTable,\n\tcolours: ReadonlyArray<BandChannels | undefined>,\n\tview: BandView,\n): BandStrip | undefined {\n\tif (view.pixelsPerSecond <= 0 || !colours.some(Boolean)) return undefined;\n\n\tconst { bandCount, framesPerSecond, levels } = table;\n\tconst mixGroup = groupMixer(table, colours, view.mix ?? { contrast: 1, normalize: 1, tilt: 0 });\n\tconst pxPerFrame = view.pixelsPerSecond / framesPerSecond;\n\tconst frames = Math.max(1, Math.round(1 / pxPerFrame));\n\tconst groupWidth = frames * pxPerFrame;\n\tconst openingFrame = view.startSeconds * framesPerSecond;\n\tconst groupsTotal = Math.ceil(levels.length / bandCount / frames);\n\t// A group past each end, so the smoothing at the image's own edges falls outside the window\n\tconst first = Math.max(0, Math.floor(openingFrame / frames) - 1);\n\tconst last = Math.min(\n\t\tgroupsTotal,\n\t\tMath.ceil((openingFrame + view.width / pxPerFrame) / frames) + 1,\n\t);\n\tconst count = last - first;\n\tif (count < 1) return undefined;\n\n\tconst pixels = new Uint8ClampedArray(count * 4);\n\n\tfor (let index = 0; index < count; index += 1) {\n\t\tconst fromFrame = (first + index) * frames;\n\t\tconst mixed = mixGroup(fromFrame, fromFrame + frames);\n\n\t\tif (mixed) pixels.set(mixed, index * 4);\n\t}\n\n\treturn { count, groupWidth, pixels, x: (first * frames - openingFrame) * pxPerFrame };\n}\n"],"mappings":"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"}
|
|
@@ -9,7 +9,10 @@ export declare const waveformColours: {
|
|
|
9
9
|
readonly wave: "--_sonic-waveform-wave";
|
|
10
10
|
};
|
|
11
11
|
export declare const waveformNumbers: {
|
|
12
|
+
readonly bandContrast: "--_sonic-waveform-band-contrast";
|
|
13
|
+
readonly bandNormalize: "--_sonic-waveform-band-normalize";
|
|
12
14
|
readonly bandOpacity: "--_sonic-waveform-band-opacity";
|
|
15
|
+
readonly bandTilt: "--_sonic-waveform-band-tilt";
|
|
13
16
|
readonly edgeShade: "--_sonic-waveform-edge-shade";
|
|
14
17
|
};
|
|
15
18
|
//#endregion
|
|
@@ -14,7 +14,10 @@ const waveformColours = {
|
|
|
14
14
|
wave: "--_sonic-waveform-wave"
|
|
15
15
|
};
|
|
16
16
|
const waveformNumbers = {
|
|
17
|
+
bandContrast: "--_sonic-waveform-band-contrast",
|
|
18
|
+
bandNormalize: "--_sonic-waveform-band-normalize",
|
|
17
19
|
bandOpacity: "--_sonic-waveform-band-opacity",
|
|
20
|
+
bandTilt: "--_sonic-waveform-band-tilt",
|
|
18
21
|
edgeShade: "--_sonic-waveform-edge-shade"
|
|
19
22
|
};
|
|
20
23
|
const amplitudeMargin = .94;
|
|
@@ -71,7 +74,15 @@ function bandChannels(strip, colour) {
|
|
|
71
74
|
];
|
|
72
75
|
}
|
|
73
76
|
function paintTint(context, scene, tint) {
|
|
74
|
-
const
|
|
77
|
+
const { bandContrast, bandNormalize, bandTilt } = scene.numbers;
|
|
78
|
+
const strip = bandStrip(tint.table, tint.colours, {
|
|
79
|
+
...scene,
|
|
80
|
+
mix: {
|
|
81
|
+
contrast: bandContrast,
|
|
82
|
+
normalize: bandNormalize,
|
|
83
|
+
tilt: bandTilt
|
|
84
|
+
}
|
|
85
|
+
});
|
|
75
86
|
if (!strip) return;
|
|
76
87
|
const { canvas } = tint.strip;
|
|
77
88
|
const { count } = strip;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waveform-scene.js","names":[],"sources":["../../../src/lib/waveform-scene.ts"],"sourcesContent":["import type { View } from '#lib/frame-timeline.ts';\nimport type { BandChannels, BandTable } from '#lib/waveform-bands.ts';\nimport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nimport { isKind } from '#lib/marker-band.ts';\nimport { clamp } from '#lib/math.ts';\nimport { bandStrip } from '#lib/waveform-bands.ts';\nimport { waveformBuckets } from '#lib/waveform-buckets.ts';\n\ntype Colour = keyof typeof waveformColours;\n\ntype Numeric = keyof typeof waveformNumbers;\n\ninterface Scene extends View {\n\tcolours: Record<Colour, string>;\n\tnumbers: Record<Numeric, number>;\n}\n\nexport interface WaveformTint {\n\tcolours: ReadonlyArray<BandChannels | undefined>;\n\tstrip: CanvasRenderingContext2D;\n\ttable: BandTable;\n}\n\ninterface Drawn {\n\tbands: undefined | WaveformTint;\n\tkindColours: ReadonlyMap<string, string>;\n\tmarkers: ReadonlyArray<{ dimmed?: boolean; kind?: string; start: number }>;\n\tpeaks: undefined | WaveformPeaks;\n\trange: [number, number];\n}\n\nexport const waveformColours = {\n\tends: '--_sonic-waveform-ends',\n\tgrid: '--_sonic-waveform-grid',\n\tmarker: '--_sonic-waveform-marker',\n\tplaceholder: '--_sonic-waveform-placeholder',\n\tshade: '--_sonic-waveform-shade',\n\tshadeClear: '--_sonic-waveform-shade-clear',\n\twave: '--_sonic-waveform-wave',\n} as const;\n\nexport const waveformNumbers = {\n\tbandOpacity: '--_sonic-waveform-band-opacity',\n\tedgeShade: '--_sonic-waveform-edge-shade',\n} as const;\n\nconst amplitudeMargin = 0.94;\n\nconst hatchPitchPx = 8;\nconst placeholderWavelengthPx = 48;\nconst placeholderAmplitude = 0.2;\n\nfunction paintHatch(\n\tcontext: CanvasRenderingContext2D,\n\tscene: Scene,\n\t[from, to]: [number, number],\n): void {\n\tif (to <= from) return;\n\n\tconst { dpr, height } = scene;\n\tconst pitch = Math.max(2, Math.round(hatchPitchPx * dpr));\n\tconst offset = scene.startSeconds * scene.pixelsPerSecond;\n\n\tcontext.save();\n\tcontext.beginPath();\n\tcontext.rect(from, 0, to - from, height);\n\tcontext.clip();\n\tcontext.beginPath();\n\tfor (let x = Math.floor((from - height + offset) / pitch) * pitch - offset; x < to; x += pitch) {\n\t\tcontext.moveTo(x, height);\n\t\tcontext.lineTo(x + height, 0);\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.ends;\n\tcontext.stroke();\n\tcontext.restore();\n}\n\nfunction paintPlaceholders(context: CanvasRenderingContext2D, scene: Scene): void {\n\tif (scene.pending.length === 0) return;\n\n\tconst { dpr, height, pixelsPerSecond, startSeconds } = scene;\n\tconst wavelength = placeholderWavelengthPx * dpr;\n\tconst center = height / 2;\n\tconst origin = startSeconds * pixelsPerSecond;\n\tconst waveY = (x: number): number =>\n\t\tcenter -\n\t\tMath.sin(((x + origin) / wavelength + scene.phase) * 2 * Math.PI) *\n\t\t\tcenter *\n\t\t\tplaceholderAmplitude;\n\n\tcontext.beginPath();\n\tfor (const [from, to] of scene.pending) {\n\t\tconst opening = (from - startSeconds) * pixelsPerSecond;\n\t\tconst closing = (to - startSeconds) * pixelsPerSecond;\n\n\t\tcontext.moveTo(opening, waveY(opening));\n\t\tfor (let x = opening + dpr; x < closing; x += dpr) context.lineTo(x, waveY(x));\n\t\tcontext.lineTo(closing, waveY(closing));\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.placeholder;\n\tcontext.stroke();\n}\n\n// A painted pixel is the one reading that resolves any colour syntax to channels\nexport function bandChannels(\n\tstrip: CanvasRenderingContext2D,\n\tcolour: string,\n): BandChannels | undefined {\n\tstrip.clearRect(0, 0, 1, 1);\n\tstrip.fillStyle = colour;\n\tstrip.fillRect(0, 0, 1, 1);\n\n\tconst [red = 0, green = 0, blue = 0, alpha = 0] = strip.getImageData(0, 0, 1, 1).data;\n\n\treturn alpha === 0 ? undefined : [red, green, blue];\n}\n\nfunction paintTint(context: CanvasRenderingContext2D, scene: Scene, tint: WaveformTint): void {\n\tconst strip = bandStrip(tint.table, tint.colours, scene);\n\tif (!strip) return;\n\n\tconst { canvas } = tint.strip;\n\tconst { count } = strip;\n\n\tif (canvas.width < count) canvas.width = count;\n\ttint.strip.putImageData(new ImageData(strip.pixels, count, 1), 0, 0);\n\tcontext.globalAlpha = scene.numbers.bandOpacity;\n\tcontext.drawImage(canvas, 0, 0, count, 1, strip.x, 0, count * strip.groupWidth, scene.height);\n}\n\nfunction paintEdgeShade(context: CanvasRenderingContext2D, scene: Scene): void {\n\tconst { shade, shadeClear } = scene.colours;\n\tconst gradient = context.createLinearGradient(0, 0, 0, scene.height);\n\n\tgradient.addColorStop(0, shade);\n\tgradient.addColorStop(0.5, shadeClear);\n\tgradient.addColorStop(1, shade);\n\tcontext.globalAlpha = scene.numbers.edgeShade;\n\tcontext.fillStyle = gradient;\n\tcontext.fillRect(0, 0, scene.width, scene.height);\n}\n\nfunction paintWave(context: CanvasRenderingContext2D, scene: Scene, drawn: Drawn): void {\n\tconst buckets = drawn.peaks ? waveformBuckets(drawn.peaks, scene) : [];\n\tconst [first] = buckets;\n\tif (!first) return;\n\n\tconst center = scene.height / 2;\n\tconst amplitudePx = center * amplitudeMargin;\n\tconst { bandOpacity, edgeShade } = scene.numbers;\n\n\tcontext.fillStyle = scene.colours.wave;\n\tcontext.beginPath();\n\tcontext.moveTo(first.x, center);\n\tfor (const bucket of buckets) context.lineTo(bucket.x, center - bucket.high * amplitudePx);\n\tfor (const bucket of buckets.toReversed())\n\t\tcontext.lineTo(bucket.x, center - bucket.low * amplitudePx);\n\tcontext.closePath();\n\tcontext.fill();\n\t// The flat envelope is the mask: from here only the pixels it covers take colour\n\tcontext.globalCompositeOperation = 'source-atop';\n\tif (drawn.bands && bandOpacity > 0) paintTint(context, scene, drawn.bands);\n\tif (edgeShade > 0) paintEdgeShade(context, scene);\n\tcontext.globalAlpha = 1;\n}\n\nfunction paintMarkers(context: CanvasRenderingContext2D, scene: Scene, drawn: Drawn): void {\n\tconst line = Math.max(1, Math.round(scene.dpr));\n\n\tfor (const { dimmed, kind, start } of drawn.markers) {\n\t\tconst x = (start - scene.startSeconds) * scene.pixelsPerSecond;\n\n\t\tcontext.fillStyle =\n\t\t\t(isKind(kind) ? drawn.kindColours.get(kind) : undefined) ?? scene.colours.marker;\n\t\tcontext.globalAlpha = dimmed === true ? 0.5 : 1;\n\t\tcontext.fillRect(x - line / 2, 0, line, scene.height);\n\t}\n\tcontext.globalAlpha = 1;\n}\n\nexport function paintWaveform(context: CanvasRenderingContext2D, scene: Scene, drawn: Drawn): void {\n\tconst { dpr, height, pixelsPerSecond, startSeconds, width } = scene;\n\tconst x = (seconds: number): number => (seconds - startSeconds) * pixelsPerSecond;\n\tconst { ends, grid } = scene.colours;\n\tconst line = Math.max(1, Math.round(dpr));\n\tconst [opening, closing] = [x(drawn.range[0]), x(drawn.range[1])];\n\tconst [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];\n\n\tcontext.clearRect(0, 0, width, height);\n\tpaintWave(context, scene, drawn);\n\t// Laid under the wave, so the nearest is painted first\n\tcontext.globalCompositeOperation = 'destination-over';\n\tpaintPlaceholders(context, scene);\n\tcontext.fillStyle = grid;\n\tif (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);\n\tpaintHatch(context, scene, [0, from]);\n\tpaintHatch(context, scene, [to, width]);\n\tcontext.globalCompositeOperation = 'source-over';\n\t// Scrolling lines sit at fractional x; rounding them judders\n\tcontext.fillStyle = ends;\n\tfor (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);\n\tpaintMarkers(context, scene, drawn);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"waveform-scene.js","names":[],"sources":["../../../src/lib/waveform-scene.ts"],"sourcesContent":["import type { View } from '#lib/frame-timeline.ts';\nimport type { BandChannels, BandTable } from '#lib/waveform-bands.ts';\nimport type { WaveformPeaks } from '#lib/waveform-buckets.ts';\n\nimport { isKind } from '#lib/marker-band.ts';\nimport { clamp } from '#lib/math.ts';\nimport { bandStrip } from '#lib/waveform-bands.ts';\nimport { waveformBuckets } from '#lib/waveform-buckets.ts';\n\ntype Colour = keyof typeof waveformColours;\n\ntype Numeric = keyof typeof waveformNumbers;\n\ninterface Scene extends View {\n\tcolours: Record<Colour, string>;\n\tnumbers: Record<Numeric, number>;\n}\n\nexport interface WaveformTint {\n\tcolours: ReadonlyArray<BandChannels | undefined>;\n\tstrip: CanvasRenderingContext2D;\n\ttable: BandTable;\n}\n\ninterface Drawn {\n\tbands: undefined | WaveformTint;\n\tkindColours: ReadonlyMap<string, string>;\n\tmarkers: ReadonlyArray<{ dimmed?: boolean; kind?: string; start: number }>;\n\tpeaks: undefined | WaveformPeaks;\n\trange: [number, number];\n}\n\nexport const waveformColours = {\n\tends: '--_sonic-waveform-ends',\n\tgrid: '--_sonic-waveform-grid',\n\tmarker: '--_sonic-waveform-marker',\n\tplaceholder: '--_sonic-waveform-placeholder',\n\tshade: '--_sonic-waveform-shade',\n\tshadeClear: '--_sonic-waveform-shade-clear',\n\twave: '--_sonic-waveform-wave',\n} as const;\n\nexport const waveformNumbers = {\n\tbandContrast: '--_sonic-waveform-band-contrast',\n\tbandNormalize: '--_sonic-waveform-band-normalize',\n\tbandOpacity: '--_sonic-waveform-band-opacity',\n\tbandTilt: '--_sonic-waveform-band-tilt',\n\tedgeShade: '--_sonic-waveform-edge-shade',\n} as const;\n\nconst amplitudeMargin = 0.94;\n\nconst hatchPitchPx = 8;\nconst placeholderWavelengthPx = 48;\nconst placeholderAmplitude = 0.2;\n\nfunction paintHatch(\n\tcontext: CanvasRenderingContext2D,\n\tscene: Scene,\n\t[from, to]: [number, number],\n): void {\n\tif (to <= from) return;\n\n\tconst { dpr, height } = scene;\n\tconst pitch = Math.max(2, Math.round(hatchPitchPx * dpr));\n\tconst offset = scene.startSeconds * scene.pixelsPerSecond;\n\n\tcontext.save();\n\tcontext.beginPath();\n\tcontext.rect(from, 0, to - from, height);\n\tcontext.clip();\n\tcontext.beginPath();\n\tfor (let x = Math.floor((from - height + offset) / pitch) * pitch - offset; x < to; x += pitch) {\n\t\tcontext.moveTo(x, height);\n\t\tcontext.lineTo(x + height, 0);\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.ends;\n\tcontext.stroke();\n\tcontext.restore();\n}\n\nfunction paintPlaceholders(context: CanvasRenderingContext2D, scene: Scene): void {\n\tif (scene.pending.length === 0) return;\n\n\tconst { dpr, height, pixelsPerSecond, startSeconds } = scene;\n\tconst wavelength = placeholderWavelengthPx * dpr;\n\tconst center = height / 2;\n\tconst origin = startSeconds * pixelsPerSecond;\n\tconst waveY = (x: number): number =>\n\t\tcenter -\n\t\tMath.sin(((x + origin) / wavelength + scene.phase) * 2 * Math.PI) *\n\t\t\tcenter *\n\t\t\tplaceholderAmplitude;\n\n\tcontext.beginPath();\n\tfor (const [from, to] of scene.pending) {\n\t\tconst opening = (from - startSeconds) * pixelsPerSecond;\n\t\tconst closing = (to - startSeconds) * pixelsPerSecond;\n\n\t\tcontext.moveTo(opening, waveY(opening));\n\t\tfor (let x = opening + dpr; x < closing; x += dpr) context.lineTo(x, waveY(x));\n\t\tcontext.lineTo(closing, waveY(closing));\n\t}\n\tcontext.lineWidth = Math.max(1, Math.round(dpr));\n\tcontext.strokeStyle = scene.colours.placeholder;\n\tcontext.stroke();\n}\n\n// A painted pixel is the one reading that resolves any colour syntax to channels\nexport function bandChannels(\n\tstrip: CanvasRenderingContext2D,\n\tcolour: string,\n): BandChannels | undefined {\n\tstrip.clearRect(0, 0, 1, 1);\n\tstrip.fillStyle = colour;\n\tstrip.fillRect(0, 0, 1, 1);\n\n\tconst [red = 0, green = 0, blue = 0, alpha = 0] = strip.getImageData(0, 0, 1, 1).data;\n\n\treturn alpha === 0 ? undefined : [red, green, blue];\n}\n\nfunction paintTint(context: CanvasRenderingContext2D, scene: Scene, tint: WaveformTint): void {\n\tconst { bandContrast, bandNormalize, bandTilt } = scene.numbers;\n\tconst strip = bandStrip(tint.table, tint.colours, {\n\t\t...scene,\n\t\tmix: { contrast: bandContrast, normalize: bandNormalize, tilt: bandTilt },\n\t});\n\tif (!strip) return;\n\n\tconst { canvas } = tint.strip;\n\tconst { count } = strip;\n\n\tif (canvas.width < count) canvas.width = count;\n\ttint.strip.putImageData(new ImageData(strip.pixels, count, 1), 0, 0);\n\tcontext.globalAlpha = scene.numbers.bandOpacity;\n\tcontext.drawImage(canvas, 0, 0, count, 1, strip.x, 0, count * strip.groupWidth, scene.height);\n}\n\nfunction paintEdgeShade(context: CanvasRenderingContext2D, scene: Scene): void {\n\tconst { shade, shadeClear } = scene.colours;\n\tconst gradient = context.createLinearGradient(0, 0, 0, scene.height);\n\n\tgradient.addColorStop(0, shade);\n\tgradient.addColorStop(0.5, shadeClear);\n\tgradient.addColorStop(1, shade);\n\tcontext.globalAlpha = scene.numbers.edgeShade;\n\tcontext.fillStyle = gradient;\n\tcontext.fillRect(0, 0, scene.width, scene.height);\n}\n\nfunction paintWave(context: CanvasRenderingContext2D, scene: Scene, drawn: Drawn): void {\n\tconst buckets = drawn.peaks ? waveformBuckets(drawn.peaks, scene) : [];\n\tconst [first] = buckets;\n\tif (!first) return;\n\n\tconst center = scene.height / 2;\n\tconst amplitudePx = center * amplitudeMargin;\n\tconst { bandOpacity, edgeShade } = scene.numbers;\n\n\tcontext.fillStyle = scene.colours.wave;\n\tcontext.beginPath();\n\tcontext.moveTo(first.x, center);\n\tfor (const bucket of buckets) context.lineTo(bucket.x, center - bucket.high * amplitudePx);\n\tfor (const bucket of buckets.toReversed())\n\t\tcontext.lineTo(bucket.x, center - bucket.low * amplitudePx);\n\tcontext.closePath();\n\tcontext.fill();\n\t// The flat envelope is the mask: from here only the pixels it covers take colour\n\tcontext.globalCompositeOperation = 'source-atop';\n\tif (drawn.bands && bandOpacity > 0) paintTint(context, scene, drawn.bands);\n\tif (edgeShade > 0) paintEdgeShade(context, scene);\n\tcontext.globalAlpha = 1;\n}\n\nfunction paintMarkers(context: CanvasRenderingContext2D, scene: Scene, drawn: Drawn): void {\n\tconst line = Math.max(1, Math.round(scene.dpr));\n\n\tfor (const { dimmed, kind, start } of drawn.markers) {\n\t\tconst x = (start - scene.startSeconds) * scene.pixelsPerSecond;\n\n\t\tcontext.fillStyle =\n\t\t\t(isKind(kind) ? drawn.kindColours.get(kind) : undefined) ?? scene.colours.marker;\n\t\tcontext.globalAlpha = dimmed === true ? 0.5 : 1;\n\t\tcontext.fillRect(x - line / 2, 0, line, scene.height);\n\t}\n\tcontext.globalAlpha = 1;\n}\n\nexport function paintWaveform(context: CanvasRenderingContext2D, scene: Scene, drawn: Drawn): void {\n\tconst { dpr, height, pixelsPerSecond, startSeconds, width } = scene;\n\tconst x = (seconds: number): number => (seconds - startSeconds) * pixelsPerSecond;\n\tconst { ends, grid } = scene.colours;\n\tconst line = Math.max(1, Math.round(dpr));\n\tconst [opening, closing] = [x(drawn.range[0]), x(drawn.range[1])];\n\tconst [from, to] = [clamp(opening, 0, width), clamp(closing, 0, width)];\n\n\tcontext.clearRect(0, 0, width, height);\n\tpaintWave(context, scene, drawn);\n\t// Laid under the wave, so the nearest is painted first\n\tcontext.globalCompositeOperation = 'destination-over';\n\tpaintPlaceholders(context, scene);\n\tcontext.fillStyle = grid;\n\tif (to > from) context.fillRect(from, Math.round((height - line) / 2), to - from, line);\n\tpaintHatch(context, scene, [0, from]);\n\tpaintHatch(context, scene, [to, width]);\n\tcontext.globalCompositeOperation = 'source-over';\n\t// Scrolling lines sit at fractional x; rounding them judders\n\tcontext.fillStyle = ends;\n\tfor (const edge of [opening, closing]) context.fillRect(edge - line / 2, 0, line, height);\n\tpaintMarkers(context, scene, drawn);\n}\n"],"mappings":";;;;;;AAgCA,MAAa,kBAAkB;CAC9B,MAAM;CACN,MAAM;CACN,QAAQ;CACR,aAAa;CACb,OAAO;CACP,YAAY;CACZ,MAAM;AACP;AAEA,MAAa,kBAAkB;CAC9B,cAAc;CACd,eAAe;CACf,aAAa;CACb,UAAU;CACV,WAAW;AACZ;AAEA,MAAM,kBAAkB;AAExB,MAAM,eAAe;AACrB,MAAM,0BAA0B;AAChC,MAAM,uBAAuB;AAE7B,SAAS,WACR,SACA,OACA,CAAC,MAAM,KACA;CACP,IAAI,MAAM,MAAM;CAEhB,MAAM,EAAE,KAAK,WAAW;CACxB,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,eAAe,GAAG,CAAC;CACxD,MAAM,SAAS,MAAM,eAAe,MAAM;CAE1C,QAAQ,KAAK;CACb,QAAQ,UAAU;CAClB,QAAQ,KAAK,MAAM,GAAG,KAAK,MAAM,MAAM;CACvC,QAAQ,KAAK;CACb,QAAQ,UAAU;CAClB,KAAK,IAAI,IAAI,KAAK,OAAO,OAAO,SAAS,UAAU,KAAK,IAAI,QAAQ,QAAQ,IAAI,IAAI,KAAK,OAAO;EAC/F,QAAQ,OAAO,GAAG,MAAM;EACxB,QAAQ,OAAO,IAAI,QAAQ,CAAC;CAC7B;CACA,QAAQ,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,GAAG,CAAC;CAC/C,QAAQ,cAAc,MAAM,QAAQ;CACpC,QAAQ,OAAO;CACf,QAAQ,QAAQ;AACjB;AAEA,SAAS,kBAAkB,SAAmC,OAAoB;CACjF,IAAI,MAAM,QAAQ,WAAW,GAAG;CAEhC,MAAM,EAAE,KAAK,QAAQ,iBAAiB,iBAAiB;CACvD,MAAM,aAAa,0BAA0B;CAC7C,MAAM,SAAS,SAAS;CACxB,MAAM,SAAS,eAAe;CAC9B,MAAM,SAAS,MACd,SACA,KAAK,MAAM,IAAI,UAAU,aAAa,MAAM,SAAS,IAAI,KAAK,EAAE,IAC/D,SACA;CAEF,QAAQ,UAAU;CAClB,KAAK,MAAM,CAAC,MAAM,OAAO,MAAM,SAAS;EACvC,MAAM,WAAW,OAAO,gBAAgB;EACxC,MAAM,WAAW,KAAK,gBAAgB;EAEtC,QAAQ,OAAO,SAAS,MAAM,OAAO,CAAC;EACtC,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI,SAAS,KAAK,KAAK,QAAQ,OAAO,GAAG,MAAM,CAAC,CAAC;EAC7E,QAAQ,OAAO,SAAS,MAAM,OAAO,CAAC;CACvC;CACA,QAAQ,YAAY,KAAK,IAAI,GAAG,KAAK,MAAM,GAAG,CAAC;CAC/C,QAAQ,cAAc,MAAM,QAAQ;CACpC,QAAQ,OAAO;AAChB;AAGA,SAAgB,aACf,OACA,QAC2B;CAC3B,MAAM,UAAU,GAAG,GAAG,GAAG,CAAC;CAC1B,MAAM,YAAY;CAClB,MAAM,SAAS,GAAG,GAAG,GAAG,CAAC;CAEzB,MAAM,CAAC,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,KAAK,MAAM,aAAa,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC;CAEjF,OAAO,UAAU,IAAI,SAAY;EAAC;EAAK;EAAO;CAAI;AACnD;AAEA,SAAS,UAAU,SAAmC,OAAc,MAA0B;CAC7F,MAAM,EAAE,cAAc,eAAe,aAAa,MAAM;CACxD,MAAM,QAAQ,UAAU,KAAK,OAAO,KAAK,SAAS;EACjD,GAAG;EACH,KAAK;GAAE,UAAU;GAAc,WAAW;GAAe,MAAM;EAAS;CACzE,CAAC;CACD,IAAI,CAAC,OAAO;CAEZ,MAAM,EAAE,WAAW,KAAK;CACxB,MAAM,EAAE,UAAU;CAElB,IAAI,OAAO,QAAQ,OAAO,OAAO,QAAQ;CACzC,KAAK,MAAM,aAAa,IAAI,UAAU,MAAM,QAAQ,OAAO,CAAC,GAAG,GAAG,CAAC;CACnE,QAAQ,cAAc,MAAM,QAAQ;CACpC,QAAQ,UAAU,QAAQ,GAAG,GAAG,OAAO,GAAG,MAAM,GAAG,GAAG,QAAQ,MAAM,YAAY,MAAM,MAAM;AAC7F;AAEA,SAAS,eAAe,SAAmC,OAAoB;CAC9E,MAAM,EAAE,OAAO,eAAe,MAAM;CACpC,MAAM,WAAW,QAAQ,qBAAqB,GAAG,GAAG,GAAG,MAAM,MAAM;CAEnE,SAAS,aAAa,GAAG,KAAK;CAC9B,SAAS,aAAa,IAAK,UAAU;CACrC,SAAS,aAAa,GAAG,KAAK;CAC9B,QAAQ,cAAc,MAAM,QAAQ;CACpC,QAAQ,YAAY;CACpB,QAAQ,SAAS,GAAG,GAAG,MAAM,OAAO,MAAM,MAAM;AACjD;AAEA,SAAS,UAAU,SAAmC,OAAc,OAAoB;CACvF,MAAM,UAAU,MAAM,QAAQ,gBAAgB,MAAM,OAAO,KAAK,IAAI,CAAC;CACrE,MAAM,CAAC,SAAS;CAChB,IAAI,CAAC,OAAO;CAEZ,MAAM,SAAS,MAAM,SAAS;CAC9B,MAAM,cAAc,SAAS;CAC7B,MAAM,EAAE,aAAa,cAAc,MAAM;CAEzC,QAAQ,YAAY,MAAM,QAAQ;CAClC,QAAQ,UAAU;CAClB,QAAQ,OAAO,MAAM,GAAG,MAAM;CAC9B,KAAK,MAAM,UAAU,SAAS,QAAQ,OAAO,OAAO,GAAG,SAAS,OAAO,OAAO,WAAW;CACzF,KAAK,MAAM,UAAU,QAAQ,WAAW,GACvC,QAAQ,OAAO,OAAO,GAAG,SAAS,OAAO,MAAM,WAAW;CAC3D,QAAQ,UAAU;CAClB,QAAQ,KAAK;CAEb,QAAQ,2BAA2B;CACnC,IAAI,MAAM,SAAS,cAAc,GAAG,UAAU,SAAS,OAAO,MAAM,KAAK;CACzE,IAAI,YAAY,GAAG,eAAe,SAAS,KAAK;CAChD,QAAQ,cAAc;AACvB;AAEA,SAAS,aAAa,SAAmC,OAAc,OAAoB;CAC1F,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,GAAG,CAAC;CAE9C,KAAK,MAAM,EAAE,QAAQ,MAAM,WAAW,MAAM,SAAS;EACpD,MAAM,KAAK,QAAQ,MAAM,gBAAgB,MAAM;EAE/C,QAAQ,aACN,OAAO,IAAI,IAAI,MAAM,YAAY,IAAI,IAAI,IAAI,WAAc,MAAM,QAAQ;EAC3E,QAAQ,cAAc,WAAW,OAAO,KAAM;EAC9C,QAAQ,SAAS,IAAI,OAAO,GAAG,GAAG,MAAM,MAAM,MAAM;CACrD;CACA,QAAQ,cAAc;AACvB;AAEA,SAAgB,cAAc,SAAmC,OAAc,OAAoB;CAClG,MAAM,EAAE,KAAK,QAAQ,iBAAiB,cAAc,UAAU;CAC9D,MAAM,KAAK,aAA6B,UAAU,gBAAgB;CAClE,MAAM,EAAE,MAAM,SAAS,MAAM;CAC7B,MAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,GAAG,CAAC;CACxC,MAAM,CAAC,SAAS,WAAW,CAAC,EAAE,MAAM,MAAM,EAAE,GAAG,EAAE,MAAM,MAAM,EAAE,CAAC;CAChE,MAAM,CAAC,MAAM,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,SAAS,GAAG,KAAK,CAAC;CAEtE,QAAQ,UAAU,GAAG,GAAG,OAAO,MAAM;CACrC,UAAU,SAAS,OAAO,KAAK;CAE/B,QAAQ,2BAA2B;CACnC,kBAAkB,SAAS,KAAK;CAChC,QAAQ,YAAY;CACpB,IAAI,KAAK,MAAM,QAAQ,SAAS,MAAM,KAAK,OAAO,SAAS,QAAQ,CAAC,GAAG,KAAK,MAAM,IAAI;CACtF,WAAW,SAAS,OAAO,CAAC,GAAG,IAAI,CAAC;CACpC,WAAW,SAAS,OAAO,CAAC,IAAI,KAAK,CAAC;CACtC,QAAQ,2BAA2B;CAEnC,QAAQ,YAAY;CACpB,KAAK,MAAM,QAAQ,CAAC,SAAS,OAAO,GAAG,QAAQ,SAAS,OAAO,OAAO,GAAG,GAAG,MAAM,MAAM;CACxF,aAAa,SAAS,OAAO,KAAK;AACnC"}
|
package/dist/styles/button.css
CHANGED
|
@@ -112,11 +112,13 @@
|
|
|
112
112
|
);
|
|
113
113
|
|
|
114
114
|
color: color-mix(in oklab, var(--_sonic-lit) 55%, var(--_sonic-ink));
|
|
115
|
+
}
|
|
115
116
|
|
|
116
117
|
@media (forced-colors: active) {
|
|
118
|
+
:where([armed]) > .sonic-button:not(:disabled, [aria-disabled='true']) {
|
|
117
119
|
--_sonic-keycap-glow: transparent;
|
|
118
|
-
}
|
|
119
120
|
}
|
|
121
|
+
}
|
|
120
122
|
|
|
121
123
|
/* Before the latched rule too; a button with an LED blinks the LED and keeps its legend */
|
|
122
124
|
:where([busy]) > .sonic-button:not(:disabled, [aria-disabled='true'], :has(.sonic-led)) {
|
|
@@ -153,21 +155,28 @@
|
|
|
153
155
|
)
|
|
154
156
|
)
|
|
155
157
|
);
|
|
158
|
+
}
|
|
156
159
|
|
|
157
|
-
|
|
160
|
+
.sonic-button:is([aria-pressed='true'],[aria-expanded='true'],:where([momentary]:state(pressed)) > *) .sonic-button-cap > [data-sonic-when='released'] {
|
|
158
161
|
display: none;
|
|
159
162
|
}
|
|
160
163
|
|
|
161
164
|
@media (forced-colors: active) {
|
|
165
|
+
|
|
166
|
+
.sonic-button:is(
|
|
167
|
+
[aria-pressed='true'],
|
|
168
|
+
[aria-expanded='true'],
|
|
169
|
+
:where([momentary]:state(pressed)) > *
|
|
170
|
+
) {
|
|
162
171
|
--_sonic-skirt: var(--_sonic-lit);
|
|
163
172
|
--_sonic-face: var(--_sonic-lit);
|
|
164
173
|
--_sonic-ink: HighlightText;
|
|
174
|
+
}
|
|
165
175
|
|
|
166
|
-
|
|
176
|
+
.sonic-button:is([aria-pressed='true'],[aria-expanded='true'],:where([momentary]:state(pressed)) > *):is(:disabled,[aria-disabled='true']) {
|
|
167
177
|
--_sonic-ink: ButtonFace;
|
|
168
178
|
}
|
|
169
179
|
}
|
|
170
|
-
}
|
|
171
180
|
|
|
172
181
|
.sonic-button:is(:active, [data-sonic-pressed], :where([momentary]:state(pressed)) > *):where(
|
|
173
182
|
:not(:disabled, [aria-disabled='true'])
|
|
@@ -232,11 +241,14 @@
|
|
|
232
241
|
--_sonic-blank-edge-off,
|
|
233
242
|
var(--_sonic-blank-edge-lit, color-mix(in oklab, currentcolor 35%, transparent))
|
|
234
243
|
);
|
|
244
|
+
}
|
|
235
245
|
|
|
236
246
|
@media (forced-colors: active) {
|
|
247
|
+
|
|
248
|
+
.sonic-button:not(:has(> .sonic-button-cap > :not(.sonic-led))) {
|
|
237
249
|
--_sonic-blank-edge: transparent;
|
|
238
|
-
}
|
|
239
250
|
}
|
|
251
|
+
}
|
|
240
252
|
|
|
241
253
|
.sonic-button-cap {
|
|
242
254
|
--_sonic-keycap-corner: max(0px, var(--_sonic-button-corner) - var(--_sonic-button-gap));
|
|
@@ -259,19 +271,20 @@
|
|
|
259
271
|
inset: var(--_sonic-button-gap);
|
|
260
272
|
gap: calc(var(--_sonic-button-size) * var(--_sonic-button-led-ratio) / 2);
|
|
261
273
|
place-content: center;
|
|
274
|
+
}
|
|
262
275
|
|
|
263
|
-
|
|
276
|
+
.sonic-button-cap > .sonic-led {
|
|
264
277
|
--_sonic-led-size: var(
|
|
265
278
|
--sonic-led-size,
|
|
266
279
|
calc(var(--_sonic-button-size) * var(--_sonic-button-led-ratio))
|
|
267
280
|
);
|
|
268
281
|
}
|
|
269
282
|
|
|
270
|
-
|
|
283
|
+
.sonic-button-cap > :not(.sonic-led) {
|
|
271
284
|
filter: var(--_sonic-engraving) var(--_sonic-button-legend-glow,);
|
|
272
285
|
}
|
|
273
286
|
|
|
274
|
-
|
|
287
|
+
.sonic-button-cap > :not(svg,.sonic-led) {
|
|
275
288
|
padding-inline: calc(var(--_sonic-button-size) * var(--_sonic-button-padding-ratio));
|
|
276
289
|
font-size: var(
|
|
277
290
|
--_sonic-button-font-own,
|
|
@@ -284,7 +297,7 @@
|
|
|
284
297
|
white-space: nowrap;
|
|
285
298
|
}
|
|
286
299
|
|
|
287
|
-
|
|
300
|
+
.sonic-button-cap > svg {
|
|
288
301
|
inline-size: auto;
|
|
289
302
|
block-size: calc(var(--_sonic-button-size) * var(--_sonic-button-icon-ratio));
|
|
290
303
|
aspect-ratio: auto 1;
|
|
@@ -295,7 +308,6 @@
|
|
|
295
308
|
);
|
|
296
309
|
mask-repeat: no-repeat;
|
|
297
310
|
}
|
|
298
|
-
}
|
|
299
311
|
|
|
300
312
|
.sonic-button-cap:has(> :not(.sonic-led)) {
|
|
301
313
|
position: relative;
|
|
@@ -329,11 +341,18 @@
|
|
|
329
341
|
[busy] > .sonic-button:not(:disabled, [aria-disabled='true']) .sonic-led::after {
|
|
330
342
|
animation: sonic-button-busy 500ms ease-in-out var(--sonic-button-busy-delay, 0s) infinite
|
|
331
343
|
alternate;
|
|
344
|
+
}
|
|
332
345
|
|
|
333
346
|
@media (prefers-reduced-motion: reduce) {
|
|
347
|
+
|
|
348
|
+
[busy]
|
|
349
|
+
> .sonic-button:not(:disabled, [aria-disabled='true'], :has(.sonic-led))
|
|
350
|
+
> .sonic-button-cap
|
|
351
|
+
> *,
|
|
352
|
+
[busy] > .sonic-button:not(:disabled, [aria-disabled='true']) .sonic-led::after {
|
|
334
353
|
animation: none;
|
|
335
|
-
}
|
|
336
354
|
}
|
|
355
|
+
}
|
|
337
356
|
|
|
338
357
|
@media (forced-colors: active) {
|
|
339
358
|
.sonic-button-cap > :not(.sonic-led) {
|
package/dist/styles/dial.css
CHANGED
|
@@ -148,8 +148,9 @@
|
|
|
148
148
|
.sonic-dial-scale {
|
|
149
149
|
font-size: calc(var(--_sonic-dial-size) * var(--_sonic-dial-scale-font-ratio));
|
|
150
150
|
line-height: 1cap;
|
|
151
|
+
}
|
|
151
152
|
|
|
152
|
-
|
|
153
|
+
.sonic-dial-scale > * {
|
|
153
154
|
--_sonic-dial-scale-angle: calc(
|
|
154
155
|
var(--_sonic-dial-start) + var(--_sonic-scale-at, 0) * var(--_sonic-dial-sweep)
|
|
155
156
|
);
|
|
@@ -165,7 +166,7 @@
|
|
|
165
166
|
calc(-50% - var(--_sonic-dial-scale-cos) * (50% + 0.15cap));
|
|
166
167
|
}
|
|
167
168
|
|
|
168
|
-
|
|
169
|
+
.sonic-dial-scale > .sonic-scale-tick {
|
|
169
170
|
--_sonic-dial-tick-length: calc(var(--_sonic-dial-scale-band) * 0.4);
|
|
170
171
|
|
|
171
172
|
inline-size: var(--_sonic-unit);
|
|
@@ -174,7 +175,6 @@
|
|
|
174
175
|
translate: calc(-50% + var(--_sonic-dial-scale-sin) * var(--_sonic-dial-tick-length) / 2)
|
|
175
176
|
calc(-50% - var(--_sonic-dial-scale-cos) * var(--_sonic-dial-tick-length) / 2);
|
|
176
177
|
}
|
|
177
|
-
}
|
|
178
178
|
|
|
179
179
|
:is(.sonic-dial-ring, .sonic-dial-modulation) {
|
|
180
180
|
--_sonic-arc-width: var(--_sonic-dial-ring-width);
|
|
@@ -254,16 +254,16 @@
|
|
|
254
254
|
display: block;
|
|
255
255
|
background: none;
|
|
256
256
|
mask: none;
|
|
257
|
+
}
|
|
257
258
|
|
|
258
|
-
|
|
259
|
-
&::after {
|
|
259
|
+
:is(:state(modulated) > .sonic-dial > .sonic-dial-modulation)::before,:is(:state(modulated) > .sonic-dial > .sonic-dial-modulation)::after {
|
|
260
260
|
position: absolute;
|
|
261
261
|
inset: 0;
|
|
262
262
|
content: '';
|
|
263
263
|
border-radius: 50%;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
:is(:state(modulated) > .sonic-dial > .sonic-dial-modulation)::before {
|
|
267
267
|
background:
|
|
268
268
|
conic-gradient(
|
|
269
269
|
from var(--_sonic-dial-start),
|
|
@@ -282,7 +282,7 @@
|
|
|
282
282
|
mask: var(--_sonic-arc-mask);
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
|
|
285
|
+
:is(:state(modulated) > .sonic-dial > .sonic-dial-modulation)::after {
|
|
286
286
|
background: linear-gradient(
|
|
287
287
|
var(--_sonic-dial-modulation-tick),
|
|
288
288
|
var(--_sonic-dial-modulation-tick)
|
|
@@ -290,7 +290,6 @@
|
|
|
290
290
|
50% 0 / var(--_sonic-unit) var(--_sonic-dial-ring-width) no-repeat;
|
|
291
291
|
rotate: calc(var(--_sonic-dial-start) + var(--_sonic-dial-modulation-value-angle));
|
|
292
292
|
}
|
|
293
|
-
}
|
|
294
293
|
|
|
295
294
|
.sonic-dial-cap {
|
|
296
295
|
position: absolute;
|
|
@@ -302,15 +301,15 @@
|
|
|
302
301
|
var(--_sonic-drop-shade),
|
|
303
302
|
var(--_sonic-rim),
|
|
304
303
|
var(--_sonic-focus-shadow);
|
|
304
|
+
}
|
|
305
305
|
|
|
306
|
-
|
|
307
|
-
&::after {
|
|
306
|
+
.sonic-dial-cap::before,.sonic-dial-cap::after {
|
|
308
307
|
position: absolute;
|
|
309
308
|
content: '';
|
|
310
309
|
border-radius: 50%;
|
|
311
310
|
}
|
|
312
311
|
|
|
313
|
-
|
|
312
|
+
.sonic-dial-cap::before {
|
|
314
313
|
inset: 0;
|
|
315
314
|
background: repeating-conic-gradient(
|
|
316
315
|
rgb(from var(--_sonic-shade) r g b / calc(0.3 * var(--_sonic-relief))) 0,
|
|
@@ -322,7 +321,7 @@
|
|
|
322
321
|
rotate: var(--_sonic-dial-angle);
|
|
323
322
|
}
|
|
324
323
|
|
|
325
|
-
|
|
324
|
+
.sonic-dial-cap::after {
|
|
326
325
|
inset: 16%;
|
|
327
326
|
background:
|
|
328
327
|
conic-gradient(
|
|
@@ -343,7 +342,6 @@
|
|
|
343
342
|
var(--_sonic-face);
|
|
344
343
|
box-shadow: var(--_sonic-face-shadow);
|
|
345
344
|
}
|
|
346
|
-
}
|
|
347
345
|
|
|
348
346
|
.sonic-dial-indicator {
|
|
349
347
|
position: absolute;
|
|
@@ -351,8 +349,9 @@
|
|
|
351
349
|
pointer-events: none;
|
|
352
350
|
border-radius: 50%;
|
|
353
351
|
rotate: calc(var(--_sonic-dial-start) + var(--_sonic-dial-angle));
|
|
352
|
+
}
|
|
354
353
|
|
|
355
|
-
|
|
354
|
+
.sonic-dial-indicator::before {
|
|
356
355
|
--_sonic-dial-indicator-width: calc(
|
|
357
356
|
var(--_sonic-dial-size) * var(--_sonic-dial-indicator-ratio)
|
|
358
357
|
);
|
|
@@ -380,7 +379,6 @@
|
|
|
380
379
|
);
|
|
381
380
|
translate: -50% 0;
|
|
382
381
|
}
|
|
383
|
-
}
|
|
384
382
|
|
|
385
383
|
:disabled > .sonic-dial > .sonic-dial-indicator::before {
|
|
386
384
|
background: var(--_sonic-ink-disabled);
|
package/dist/styles/envelope.css
CHANGED
|
@@ -65,13 +65,13 @@
|
|
|
65
65
|
inset: 0;
|
|
66
66
|
overflow: clip;
|
|
67
67
|
pointer-events: none;
|
|
68
|
+
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
+
.sonic-envelope-bracket[data-sonic-part='curve'] {
|
|
70
71
|
--_sonic-point-size: calc(
|
|
71
72
|
2 * var(--_sonic-envelope-size) * var(--_sonic-envelope-curve-ratio)
|
|
72
73
|
);
|
|
73
74
|
}
|
|
74
|
-
}
|
|
75
75
|
|
|
76
76
|
.sonic-envelope-fill {
|
|
77
77
|
fill: color-mix(in oklab, var(--_sonic-lit) 18%, transparent);
|
package/dist/styles/led.css
CHANGED
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
block-size: var(--_sonic-led-size);
|
|
20
20
|
background: var(--_sonic-lens-bezel);
|
|
21
21
|
border-radius: 50%;
|
|
22
|
+
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
.sonic-led::after {
|
|
24
25
|
position: absolute;
|
|
25
26
|
inset: calc(var(--_sonic-led-size) * (1 - var(--_sonic-led-lens-ratio)) / 2);
|
|
26
27
|
content: '';
|
|
@@ -29,19 +30,20 @@
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
@media (forced-colors: active) {
|
|
33
|
+
.sonic-led {
|
|
32
34
|
--_sonic-lit-ok: Mark;
|
|
33
35
|
--_sonic-lit-warning: Highlight;
|
|
34
36
|
--_sonic-lit-danger: Mark;
|
|
35
37
|
|
|
36
38
|
forced-color-adjust: none;
|
|
37
39
|
background: none;
|
|
40
|
+
}
|
|
38
41
|
|
|
39
|
-
|
|
42
|
+
.sonic-led::after {
|
|
40
43
|
background: GrayText;
|
|
41
44
|
box-shadow: 0 0 0 1px CanvasText;
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
}
|
|
45
47
|
|
|
46
48
|
.sonic-led[data-sonic-lit='ok'] {
|
|
47
49
|
--_sonic-lit: var(--_sonic-lit-ok);
|
|
@@ -56,23 +58,18 @@
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
/* Before the lit rule, which a latched button's LED also matches */
|
|
59
|
-
|
|
60
|
-
[data-sonic-lit='idle'],
|
|
61
|
-
:where(:is([armed], [busy]) > .sonic-button:not(:disabled, [aria-disabled='true'])) *
|
|
62
|
-
) {
|
|
63
|
-
&::after {
|
|
61
|
+
.sonic-led:is([data-sonic-lit='idle'],:where(:is([armed],[busy]) > .sonic-button:not(:disabled,[aria-disabled='true'])) *)::after {
|
|
64
62
|
background:
|
|
65
63
|
var(--_sonic-lens-glint),
|
|
66
64
|
color-mix(in oklab, var(--_sonic-lit) 35%, var(--_sonic-lens-glass));
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
@media (forced-colors: active) {
|
|
70
|
-
|
|
68
|
+
.sonic-led:is([data-sonic-lit='idle'],:where(:is([armed],[busy]) > .sonic-button:not(:disabled,[aria-disabled='true'])) *)::after {
|
|
71
69
|
background: GrayText;
|
|
72
70
|
box-shadow: 0 0 0 1px var(--_sonic-lit);
|
|
73
71
|
}
|
|
74
72
|
}
|
|
75
|
-
}
|
|
76
73
|
|
|
77
74
|
.sonic-led:is(
|
|
78
75
|
[data-sonic-lit]:where(:not([data-sonic-lit='idle'])),
|
|
@@ -96,8 +93,10 @@
|
|
|
96
93
|
transparent
|
|
97
94
|
),
|
|
98
95
|
var(--_sonic-lens-bezel);
|
|
96
|
+
}
|
|
99
97
|
|
|
100
|
-
|
|
98
|
+
.sonic-led:is([data-sonic-lit]:where(:not([data-sonic-lit='idle'])),:where(.sonic-button:is([aria-pressed='true'],[aria-expanded='true']),[momentary]:state(pressed) > .sonic-button,.sonic-segmented-option[aria-checked='true'],.sonic-toggle:has( > [role='switch'][aria-checked='true'], > [aria-checked='true'] [data-sonic-lit]))
|
|
99
|
+
*,:where(:state(at-origin):not(:disabled) > .sonic-led))::after {
|
|
101
100
|
background: radial-gradient(
|
|
102
101
|
closest-side,
|
|
103
102
|
color-mix(
|
|
@@ -110,14 +109,30 @@
|
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
@media (forced-colors: active) {
|
|
112
|
+
|
|
113
|
+
.sonic-led:is(
|
|
114
|
+
[data-sonic-lit]:where(:not([data-sonic-lit='idle'])),
|
|
115
|
+
:where(
|
|
116
|
+
.sonic-button:is([aria-pressed='true'], [aria-expanded='true']),
|
|
117
|
+
[momentary]:state(pressed) > .sonic-button,
|
|
118
|
+
.sonic-segmented-option[aria-checked='true'],
|
|
119
|
+
.sonic-toggle:has(
|
|
120
|
+
> [role='switch'][aria-checked='true'],
|
|
121
|
+
> [aria-checked='true'] [data-sonic-lit]
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
*,
|
|
125
|
+
:where(:state(at-origin):not(:disabled) > .sonic-led)
|
|
126
|
+
) {
|
|
113
127
|
background: none;
|
|
128
|
+
}
|
|
114
129
|
|
|
115
|
-
|
|
130
|
+
.sonic-led:is([data-sonic-lit]:where(:not([data-sonic-lit='idle'])),:where(.sonic-button:is([aria-pressed='true'],[aria-expanded='true']),[momentary]:state(pressed) > .sonic-button,.sonic-segmented-option[aria-checked='true'],.sonic-toggle:has( > [role='switch'][aria-checked='true'], > [aria-checked='true'] [data-sonic-lit]))
|
|
131
|
+
*,:where(:state(at-origin):not(:disabled) > .sonic-led))::after {
|
|
116
132
|
background: var(--_sonic-lit);
|
|
117
133
|
box-shadow: 0 0 0 1px CanvasText;
|
|
118
134
|
}
|
|
119
135
|
}
|
|
120
|
-
}
|
|
121
136
|
|
|
122
137
|
:is(.sonic-button:is(:disabled, [aria-disabled='true']), .sonic-segmented-option:disabled)
|
|
123
138
|
.sonic-led {
|
|
@@ -97,13 +97,13 @@
|
|
|
97
97
|
|
|
98
98
|
:is(.sonic-button, .sonic-segmented-option, .sonic-switch-position, .sonic-toggle-position) {
|
|
99
99
|
--_sonic-target-outset: min(0px, calc((100% - var(--sonic-target-size, 100%)) / 2));
|
|
100
|
+
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
+
:is(.sonic-button,.sonic-segmented-option,.sonic-switch-position,.sonic-toggle-position)::after {
|
|
102
103
|
position: absolute;
|
|
103
104
|
inset: var(--_sonic-target-outset);
|
|
104
105
|
content: '';
|
|
105
106
|
}
|
|
106
|
-
}
|
|
107
107
|
|
|
108
108
|
@media not (forced-colors: active) {
|
|
109
109
|
:is(
|
|
@@ -146,12 +146,12 @@
|
|
|
146
146
|
):focus-visible {
|
|
147
147
|
outline: 2px solid transparent;
|
|
148
148
|
outline-offset: var(--sonic-focus-outline-offset, -2px);
|
|
149
|
+
}
|
|
149
150
|
|
|
150
|
-
|
|
151
|
+
:is(.sonic-dial,.sonic-button,.sonic-number,.sonic-segmented-option,.sonic-slider,.sonic-waveform,.sonic-wavestrip,.sonic-xy-axis):focus-visible:not([data-sonic-pointer-focus]) {
|
|
151
152
|
--_sonic-cap: var(--_sonic-cap-hover);
|
|
152
153
|
--_sonic-focus-shadow: var(--_sonic-focus-glow);
|
|
153
154
|
}
|
|
154
|
-
}
|
|
155
155
|
|
|
156
156
|
:is(
|
|
157
157
|
.sonic-number,
|
|
@@ -229,11 +229,11 @@
|
|
|
229
229
|
.sonic-wavestrip
|
|
230
230
|
):focus-visible {
|
|
231
231
|
outline-offset: 2px;
|
|
232
|
+
}
|
|
232
233
|
|
|
233
|
-
|
|
234
|
+
[data-sonic-pointer-focus]:is(.sonic-dial,.sonic-button,.sonic-number,.sonic-segmented-option,.sonic-slider,.sonic-waveform,.sonic-wavestrip):focus-visible {
|
|
234
235
|
outline-style: none;
|
|
235
236
|
}
|
|
236
|
-
}
|
|
237
237
|
|
|
238
238
|
:is(
|
|
239
239
|
.sonic-dial-cap,
|