Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#build--distribution)
[![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/)
[![Downloads](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=downloads)](https://github.com/CaYatur/SoundVisualizer/releases)
[![Tests](https://img.shields.io/badge/tests-2548%20passing-2ea043.svg)](#tests)
[![Tests](https://img.shields.io/badge/tests-2552%20passing-2ea043.svg)](#tests)
[![cayadev.com](https://img.shields.io/badge/cayadev.com-e11d2a.svg)](https://cayadev.com)

</div>
Expand Down Expand Up @@ -1341,7 +1341,7 @@ npm test
npm start -- --smoke
```

**2548 unit tests, all passing.** They are written to check answers, not to exercise lines:
**2552 unit tests, all passing.** They are written to check answers, not to exercise lines:

- **Formulas** are checked against values derived by hand from their definitions — Viviani's curve
staying on its sphere, the torus tube radius, Chladni's m↔n antisymmetry, every attractor
Expand Down
4 changes: 2 additions & 2 deletions README.tr.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#paketleme--dağıtım)
[![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/)
[![İndirme](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=indirme)](https://github.com/CaYatur/SoundVisualizer/releases)
[![Test](https://img.shields.io/badge/test-2548%20geçiyor-2ea043.svg)](#testler)
[![Test](https://img.shields.io/badge/test-2552%20geçiyor-2ea043.svg)](#testler)
[![cayadev.com](https://img.shields.io/badge/cayadev.com-e11d2a.svg)](https://cayadev.com)

</div>
Expand Down Expand Up @@ -1341,7 +1341,7 @@ npm test
npm start -- --smoke
```

**2548 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar:
**2552 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar:

- **Formüller**, tanımlarından elle türetilmiş değerlerle sınanıyor — Viviani eğrisinin küre
üzerinde kalması, simidin boru yarıçapı, Chladni'nin m↔n antisimetrisi, her çekicinin sınırlı
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ npm test
npm start -- --smoke
```

- **2548 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0;
- **2552 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0;
105 came with v3.1.1; 163 came with v3.1.2; 157 came with v3.1.3 — 1128 at
that tag — 469 more with v3.1.4, most of them from the MilkDrop work, and 878
on `main` since.
Expand Down
26 changes: 17 additions & 9 deletions src/admin/scene-panels.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,15 +301,15 @@
try { localStorage.setItem('sv-layers-open', JSON.stringify([...set].slice(-64))); } catch { /* yok say */ }
}

const LAYER_ICONS = { background: 'background', visualizer: 'bars', text: 'text', media: 'film', sprites: 'sparkles', logo: 'tag' };
const LAYER_ICONS = { background: 'background', visualizer: 'bars', text: 'text', media: 'film', sprites: 'sparkles', logo: 'tag', nowplaying: 'music' };

/* Katman başlığı: sıra okları, tür simgesi, ad ve özet, bayraklar, aç/kapa
ve kaldır. Liste ekranda ters sırada: "yukarı" dizide İLERİ demek (bkz.
itemHeader). Ad ve özet tıklanınca da açılıyor — küçük bir oku
hedeflemek zorunda kalınmasın. */
function layerHead(list, i, l, name, summary, flags, open, toggle, onChange) {
const el = P().el;
const icon = LAYER_ICONS[l.kind === 'visualizer' && l.type === 'text' ? 'text' : l.kind] || 'grid';
const icon = LAYER_ICONS[(l.kind === 'visualizer' && l.type === 'text') ? 'text' : (l.kind === 'visualizer' && l.type === 'nowplaying') ? 'nowplaying' : l.kind] || 'grid';
return el('div', { class: 'layer-head' + (open ? ' open' : '') }, [
el('div', { class: 'layer-ord' }, [
el('button', {
Expand Down Expand Up @@ -383,6 +383,7 @@
['media', 'Medya'],
['sprites', 'Görsel Nesneler'],
['logo', 'Logo'],
['nowplaying', 'Çalan Parça'],
];

/* Metin bir kind değil; görselleştirici türünün alt tipi. Ayrı düğmeler
Expand Down Expand Up @@ -428,6 +429,7 @@
if (kind === 'background') return MC().layerPairs('background');
if (kind === 'visualizer') return MC().layerPairs('visualizer');
if (kind === 'sprites') return [['back', 'Arka Katman'], ['front', 'Ön Katman']];
if (kind === 'nowplaying') return [];
return [];
}

Expand Down Expand Up @@ -960,10 +962,11 @@
return out;
}

if (l.kind === 'visualizer' && l.type !== 'none' && l.type !== 'custom') {
if (l.kind === 'nowplaying' || (l.kind === 'visualizer' && l.type !== 'none' && l.type !== 'custom')) {
l.settings = l.settings || {};
const defVis = def.visualizer || {};
const vs = (l.settings.visualizer = l.settings.visualizer || {});
if (l.kind === 'nowplaying') l.type = 'nowplaying';
const getV = (k, fallback) => vs[k] !== undefined ? vs[k] : (defVis[k] !== undefined ? defVis[k] : fallback);
const setV = (k, val) => { vs[k] = val; };

Expand Down Expand Up @@ -1585,12 +1588,17 @@
el('button', {
class: 'btn ghost small', type: 'button', icon: 'plus', text: label,
onclick: () => {
const opts = typeOptionsFor(kind);
const made = window.SVLayers.normalizeLayer({
kind,
name: label,
type: opts.length ? opts[0][0] : 'back',
});
let made;
if (kind === 'nowplaying' && window.SVLayers.makeNowPlayingLayer) {
made = window.SVLayers.makeNowPlayingLayer({ name: label });
} else {
const opts = typeOptionsFor(kind);
made = window.SVLayers.normalizeLayer({
kind,
name: label,
type: opts.length ? opts[0][0] : 'back',
});
}
list.push(made);
openNew(made);
rerender();
Expand Down
41 changes: 36 additions & 5 deletions src/visualizer/layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
const CSS_BLEND = (b) => (b === 'add' ? 'screen' : BLEND_MODES.indexOf(b) >= 0 ? b : 'normal');
const CANVAS_BLEND = (b) => (b === 'add' ? 'lighter' : BLEND_MODES.indexOf(b) >= 0 ? b : 'source-over');

const KINDS = ['background', 'visualizer', 'media', 'sprites', 'logo'];
const KINDS = ['background', 'visualizer', 'media', 'sprites', 'logo', 'nowplaying'];

/* Bağlamı kaybolan bir yüzey en çok bu aralıkla yeniden kuruluyor (#594). */
const REVIVE_MS = 2000;
Expand Down Expand Up @@ -93,6 +93,7 @@
out.postfx = Array.isArray(l && l.postfx) ? l.postfx : [];
out.settings = (l && l.settings) || {};
if (KINDS.indexOf(out.kind) < 0) out.kind = 'visualizer';
if (out.kind === 'nowplaying') out.type = 'nowplaying';
if (!out.id) out.id = newLayerId();
return out;
}
Expand Down Expand Up @@ -141,6 +142,23 @@
});
}

/* Now Playing layer factory — first-class kind (alongside media/logo).
Reuses SVModes.nowplaying cover/title rendering + settings.nowplaying. */
function makeNowPlayingLayer(spec) {
const s = spec || {};
const np = Object.assign({
enabled: true,
source: s.source || 'system',
coverOverlay: s.coverOverlay != null ? !!s.coverOverlay : false,
}, s.nowplaying || {});
return normalizeLayer({
name: s.name || 'Çalan Parça',
kind: 'nowplaying',
type: 'nowplaying',
settings: { nowplaying: np },
});
}

/* Eski (katmansız) yapılandırmadan katman listesi üretir.
Sıra, v2.0'daki z-index yığınının birebir aynısıdır:
arkaplan → (medya arkada) → sprite arka → görselleştirici →
Expand Down Expand Up @@ -368,6 +386,7 @@
const list = Array.isArray(cfg.layers) ? cfg.layers : [];
const vis = list.find((l) => l && l.kind === 'visualizer'
&& l.type !== 'text' && l.type !== 'nowplaying');
/* kind nowplaying is a dedicated overlay, not a classic visualizer type. */
if (cfg.visualizer && cfg.visualizer.type === 'none') {
cfg.visualizer.type = vis ? vis.type : 'bars';
}
Expand Down Expand Up @@ -502,6 +521,16 @@
});
}

if (layer.kind === 'nowplaying') {
const npSettings = (layer.settings && layer.settings.nowplaying) || {};
const defNp = (cfg && cfg.nowplaying) || (def ? def.nowplaying : {});
const baseVis = (cfg && cfg.visualizer) || (def ? def.visualizer : {}) || {};
return Object.assign({}, base, {
visualizer: Object.assign({}, baseVis, { type: 'nowplaying' }),
nowplaying: Object.assign({}, defNp, base.nowplaying, npSettings, { enabled: layer.enabled !== false }),
});
}

if (layer.kind === 'visualizer') {
const defVis = def ? def.visualizer : {};
const baseVis = (cfg && cfg.visualizer) || defVis;
Expand Down Expand Up @@ -613,7 +642,7 @@
trackArtwork = t.nowPlaying.artwork;
}
}
} else if (l.type === 'nowplaying') {
} else if (l.type === 'nowplaying' || l.kind === 'nowplaying') {
lyricsOrTextActive = true;
const np = (l.settings && l.settings.nowplaying) || cfg.nowplaying;
if (np && np.showArtwork === false) showArtworkAllowed = false;
Expand Down Expand Up @@ -1071,8 +1100,9 @@
e.ctx = e.canvas.getContext('2d');
e.solid = true; // 'solid' ya da bilinmeyen tür: düz renk
}
} else if (layer.kind === 'visualizer') {
if (window.SVModes[layer.type]) e.mode = new window.SVModes[layer.type](e.canvas);
} else if (layer.kind === 'visualizer' || layer.kind === 'nowplaying') {
const modeType = layer.kind === 'nowplaying' ? 'nowplaying' : layer.type;
if (window.SVModes[modeType]) e.mode = new window.SVModes[modeType](e.canvas);
e.ctx = e.canvas.getContext('2d');
} else {
e.ctx = e.canvas.getContext('2d');
Expand Down Expand Up @@ -1593,7 +1623,7 @@
return;
}

if (l.kind === 'visualizer') {
if (l.kind === 'visualizer' || l.kind === 'nowplaying') {
if (!e.mode) { e.ctx.clearRect(0, 0, W, H); return; }
if (!audio || !audio.ready) { e.ctx.clearRect(0, 0, W, H); return; }
e.mode.draw(audio, lcfg, t, dt);
Expand Down Expand Up @@ -1836,6 +1866,7 @@
KINDS,
normalizeLayer,
makeTextLayer,
makeNowPlayingLayer,
newLayerId,
synthesize,
resolve,
Expand Down
12 changes: 12 additions & 0 deletions tests/layers-panel-ui.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ test('aç/kapa durumu kaydediliyor, yeni eklenen katman açık geliyor', () => {
assert.ok(JSON.parse(store['sv-layers-open']).includes(made.id), 'yeni katman açık');
});


test('nowplaying add button creates nowplaying kind layer', () => {
const { panel, cfg } = setup(LAYERS);
const add = all(panel, (n) => n.tag === 'button' && /Çalan Parça$/.test(n.attrs.text || ''))[0];
assert.ok(add, 'Çalan Parça add button present');
add.attrs.onclick();
const made = cfg.layers[cfg.layers.length - 1];
assert.strictEqual(made.kind, 'nowplaying');
assert.strictEqual(made.type, 'nowplaying');
assert.ok(made.settings && made.settings.nowplaying);
});

test('kilitli katman açılamıyor', () => {
const { panel } = setup([{ id: 'k', kind: 'visualizer', type: 'bars', locked: true }], ['k']);
assert.strictEqual(all(panel, cls('layer-tabs')).length, 0);
Expand Down
22 changes: 22 additions & 0 deletions tests/layers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,28 @@ test('makeTextLayer sanatçı adı katmanı üretir', () => {
assert.strictEqual(t.align, 'left');
});


test('makeNowPlayingLayer first-class kind', () => {
const l = L.makeNowPlayingLayer({ name: 'Çalan Parça' });
assert.strictEqual(l.kind, 'nowplaying');
assert.strictEqual(l.type, 'nowplaying');
assert.strictEqual(l.name, 'Çalan Parça');
assert.ok(l.id);
assert.strictEqual(l.settings.nowplaying.enabled, true);
assert.strictEqual(l.settings.nowplaying.source, 'system');
assert.strictEqual(l.settings.nowplaying.coverOverlay, false);
});

test('normalizeLayer forces nowplaying type for nowplaying kind', () => {
const l = L.normalizeLayer({ kind: 'nowplaying', name: 'NP', type: 'bars' });
assert.strictEqual(l.kind, 'nowplaying');
assert.strictEqual(l.type, 'nowplaying');
});

test('KINDS includes nowplaying', () => {
assert.ok(L.KINDS.indexOf('nowplaying') >= 0);
});

test('yığın açıkken kullanıcı listesi kullanılır', () => {
const cfg = baseCfg();
cfg.layers = [{ id: 'a', kind: 'visualizer', type: 'wave' }];
Expand Down
Loading