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
7 changes: 6 additions & 1 deletion src/admin/admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,8 @@ body {
.adv-toggle input:checked + .track::after { left: 15px; background: #fff; }

.sections {
flex: 1; min-height: 0; overflow-y: auto; padding: 16px 20px 26px;
flex: 1; min-height: 0; min-width: 0; overflow-y: auto; overflow-x: hidden;
padding: 16px 20px 26px;
display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
gap: 14px; align-content: start;
}
Expand All @@ -314,6 +315,10 @@ body {
background: linear-gradient(180deg, var(--card), rgba(16,11,18,.9));
border: 1px solid var(--line); border-radius: var(--radius);
padding: 14px 16px 15px; align-self: start;
/* min-width: auto ızgarayı etiketin boyuna açıp yatay kaydırma çıkarıyordu. */
min-width: 0;
/* 4 px satır ızgarası kartı içeriğinden kısa sanmasın (Linux). */
height: max-content;
transition: border-color .2s, box-shadow .2s;
}
.card.wide { grid-column: 1 / -1; }
Expand Down
89 changes: 81 additions & 8 deletions src/admin/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2710,7 +2710,9 @@
icon: 'sliders',
wide: true,
title: 'Genel Işık Ayarları',
desc: 'Mod, renk ve ses tepkisi — Windows Dynamic Lighting ve OpenRGB ortak görünümü. Her ayarın hangi çıkışlarda geçerli olduğu yanında yazar. Art-Net kendi kartındaki ayarları kullanır.',
desc: isWindows()
? 'Mod, renk ve ses tepkisi — Windows Dynamic Lighting ve OpenRGB ortak görünümü. Her ayarın hangi çıkışlarda geçerli olduğu yanında yazar. Art-Net kendi kartındaki ayarları kullanır.'
: 'Mod, renk ve ses tepkisi — OpenRGB çıkışı. Art-Net kendi kartındaki ayarları kullanır.',
roots: ['lighting'],
rootOmit: ['lighting.enabled', 'lighting.deviceColors', 'lighting.deviceLedColors'],
controls: [{ type: 'lightinggeneralpanel' }],
Expand Down Expand Up @@ -3181,6 +3183,9 @@
} else {
prevScroll = root.scrollTop;
}
/* Masonry sınıfı bir önceki çizimden kalırsa yeni kartlar 4 px satıra
doğar. Linux o boyu içerik sanıp bütün kartları üst üste bindiriyor. */
root.classList.remove('masonry');
root.innerHTML = '';

const cat = CATEGORIES.find((c) => c.id === activeCategory) || CATEGORIES[0];
Expand Down Expand Up @@ -3232,6 +3237,31 @@
ölçülüyor. Yalnız kalan yarım kart tam genişlik alıyor. */
const MASONRY_ROW = 4; // px — admin.css `.sections.masonry` ile aynı
let masonryRO = null;
let sectionsRO = null;
let sectionsW = 0;

function columnCount(root) {
const tpl = getComputedStyle(root).gridTemplateColumns;
if (!tpl || tpl === 'none') return 1;
return tpl.split(/\s+/).filter(Boolean).length || 1;
}

/* Sahne Üretici ile MilkDrop Preset Üretici iki tam genişlik kartın
arasında. Pencere büyüyünce ızgara yeni sütun açar, bu ikisi birer
sütunda dar kalır. Sütun sayısı 2'yi geçince satırı yarım yarım paylaşırlar. */
function placeGeneratorPair(root) {
const a = root.querySelector(':scope > [data-card="scenegen"]');
const b = root.querySelector(':scope > [data-card="mdgen"]');
if (!a || !b) return;
a.style.gridColumn = '';
b.style.gridColumn = '';
const cols = columnCount(root);
if (cols < 3) return;
const mid = Math.ceil(cols / 2);
a.style.gridColumn = '1 / ' + (mid + 1);
b.style.gridColumn = (mid + 1) + ' / -1';
}

function layoutCards(root) {
const kids = [...root.children];
let run = [];
Expand All @@ -3242,19 +3272,62 @@
else run.push(c);
}
flush();
root.classList.add('masonry');
placeGeneratorPair(root);
const gap = parseFloat(getComputedStyle(root).columnGap) || 14;
const span = (c) => {
const h = c.getBoundingClientRect().height;
const s = 'span ' + Math.max(1, Math.ceil((h + gap) / MASONRY_ROW));
const spanFor = (h) => 'span ' + Math.max(1, Math.ceil((h + gap) / MASONRY_ROW));
/* İçerik boyu, 4 px'lik satır ızgarası kapalıyken okunur. Sınıf açıkken
Linux getBoundingClientRect'i satır yüksekliği sanıyor; span 1 kalınca
kartlar aynı yere yığılıyor. Çalan parça gibi yeniden çizen her işlem
bu yoldan geçer, o yüzden ölçüm her seferinde doğal düzende yapılır. */
root.classList.remove('masonry');
const heights = kids.map((c) => c.getBoundingClientRect().height);
root.classList.add('masonry');
kids.forEach((c, i) => {
const s = spanFor(heights[i]);
if (c.style.gridRowEnd !== s) c.style.gridRowEnd = s;
};
kids.forEach(span);
});
if (masonryRO) masonryRO.disconnect();
if (window.ResizeObserver) {
masonryRO = new ResizeObserver((entries) => { for (const e of entries) span(e.target); });
masonryRO = new ResizeObserver((entries) => {
for (const e of entries) {
const c = e.target;
if (!c.isConnected) continue;
const h = Math.max(c.scrollHeight, c.getBoundingClientRect().height);
const s = spanFor(h);
const cur = parseInt(String(c.style.gridRowEnd).replace(/[^\d]/g, ''), 10) || 0;
const next = parseInt(s.replace(/[^\d]/g, ''), 10) || 0;
/* Küçültme tam yeniden çizime bırakılır; gözlemci yalnızca
büyüyen içeriği (yüklenen kapak gibi) takip eder. Aksi halde
4 px satır ölçümü span'i eritip kartları üst üste bindirir. */
if (next > cur) c.style.gridRowEnd = s;
}
});
kids.forEach((c) => masonryRO.observe(c));
}
/* Genişlik değişince payı aynı karede yenile. Sütun sayısı aynı kalsa
da eski grid-column çizgileri yatay kaydırmayı bir sonraki çizime
kadar açık bırakıyordu. */
if (sectionsRO) sectionsRO.disconnect();
sectionsW = root.clientWidth;
if (window.ResizeObserver) {
sectionsRO = new ResizeObserver(() => {
if (!root.isConnected) return;
const w = root.clientWidth;
if (w === sectionsW) return;
sectionsW = w;
placeGeneratorPair(root);
root.classList.remove('masonry');
const again = [...root.children];
const hs = again.map((c) => c.getBoundingClientRect().height);
root.classList.add('masonry');
again.forEach((c, i) => {
const s = spanFor(hs[i]);
if (c.style.gridRowEnd !== s) c.style.gridRowEnd = s;
});
sectionsW = root.clientWidth;
});
sectionsRO.observe(root);
}
}

// Tek bir kart: başlık + gruplanmış kontroller (+ gelişmiş)
Expand Down
66 changes: 48 additions & 18 deletions src/admin/lighting-general.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
*
* Eskiden bu ayarlar yalnız WDL kartı AÇIKken görünüyordu; OpenRGB tek başına
* açıkken veya henüz hiçbir çıkış seçilmemişken dokunulamıyordu. Bu panel
* Işık kategorisinin en altında her zaman durur.
* Işık kategorisinin en altında her zaman durur. Linux ve macOS'ta kart
* kapanmaz: yalnız WDL'ye ait denetimler ve "Windows Dynamic Lighting"
* etiketi gizlenir, OpenRGB'nin kullandığı ayarlar yerinde kalır.
*
* Her denetimin yanında hangi çıkışlarda geçerli olduğu yazar. Etiketler
* kod yollarına göre: shared/lighting-render.js → WDL + OpenRGB; lighting.brightness
Expand All @@ -16,14 +18,29 @@
const WDL = 'Windows Dynamic Lighting';
const ORGB = 'OpenRGB';

const STATIC_MODES = ['single-color', 'per-device', 'per-led'];

function wdlOn() {
return !!(typeof window !== 'undefined' && window.SV_PLATFORM && window.SV_PLATFORM.isWindows);
}

function shownTags(names) {
if (wdlOn()) return names;
return names.filter((n) => n !== WDL);
}

function backends(names) {
const el = P().el;
return el('div', { class: 'lighting-backends' }, names.map((n) => el('span', { class: 'lighting-backend-tag', text: n })));
}

function wrap(ctrl, names) {
/* keep: etiket kalmayınca denetimi yine çiz (mod listesi). WDL-only
kaydırıcılar keep olmadan tamamen düşer. */
function wrap(ctrl, names, keep) {
const el = P().el;
return el('div', { class: 'lighting-tagged' }, [ctrl, backends(names)]);
const tags = shownTags(names);
if (!tags.length) return keep ? ctrl : null;
return el('div', { class: 'lighting-tagged' }, [ctrl, backends(tags)]);
}

/* Tek ayar sıfırlama: admin appendGrouped ile aynı data-path + ctrl-reset.
Expand Down Expand Up @@ -61,7 +78,9 @@
const nodes = [];
nodes.push(el('div', {
class: 'lighting-general-banner',
text: 'Bu ayarlar Windows Dynamic Lighting ve OpenRGB çıkışlarının ortak görünümüdür. Art-Net kendi kartındaki ayarları kullanır. Bir çıkışı açmadan da burada düzenleyebilirsiniz.',
text: wdlOn()
? 'Bu ayarlar Windows Dynamic Lighting ve OpenRGB çıkışlarının ortak görünümüdür. Art-Net kendi kartındaki ayarları kullanır. Bir çıkışı açmadan da burada düzenleyebilirsiniz.'
: 'Bu ayarlar OpenRGB çıkışının görünümüdür. Art-Net kendi kartındaki ayarları kullanır. Bir çıkışı açmadan da burada düzenleyebilirsiniz.',
}));

const themedDropdown = (label, value, options, onChange, description) => {
Expand Down Expand Up @@ -124,29 +143,38 @@
apply(true);
}, false), key);

const modes = (P().lightingModes && P().lightingModes()) || [];
const staticMode = ['single-color', 'per-device', 'per-led'].includes(lighting.mode);
let modes = (P().lightingModes && P().lightingModes()) || [];
if (!wdlOn()) modes = modes.filter((m) => STATIC_MODES.indexOf(String(m.value)) < 0);
const staticMode = STATIC_MODES.includes(lighting.mode);
const dynamicMode = !staticMode;

nodes.push(wrap(
attachPath(themedDropdown('Aydınlatma Modu', lighting.mode, modes, (value) => {
lighting.mode = value;
apply(true);
}, true), 'mode'),
staticMode ? [WDL] : [WDL, ORGB]
staticMode ? [WDL] : [WDL, ORGB],
!wdlOn()
));
if (staticMode) {
if (staticMode && wdlOn()) {
nodes.push(el('div', {
class: 'lighting-mode-help',
text: 'Statik modlar (tek renk, aygıt başına, LED başına) yalnız Windows Dynamic Lighting ile çalışır. OpenRGB sesi izleyen dinamik modları sürer. Aygıt/LED renk boyası Windows Dynamic Lighting kartındadır.',
}));
} else if (staticMode) {
nodes.push(el('div', {
class: 'lighting-mode-help',
text: 'Kayıtlı aydınlatma modu bu sistemde yok. Listeden OpenRGB\'nin sürebildiği bir mod seçin.',
}));
}

nodes.push(wrap(rangeRow('brightness', 'Genel Parlaklık', 0, 1, 0.01, true), [WDL]));
nodes.push(el('div', {
class: 'lighting-mode-help',
text: 'OpenRGB parlaklığı OpenRGB kartındaki Parlaklık kaydırıcısındadır; buradaki değer Windows Dynamic Lighting içindir.',
}));
if (wdlOn()) {
nodes.push(wrap(rangeRow('brightness', 'Genel Parlaklık', 0, 1, 0.01, true), [WDL]));
nodes.push(el('div', {
class: 'lighting-mode-help',
text: 'OpenRGB parlaklığı OpenRGB kartındaki Parlaklık kaydırıcısındadır; buradaki değer Windows Dynamic Lighting içindir.',
}));
}

if (lighting.mode === 'single-color') {
nodes.push(wrap(colorRow('color', 'Tek Renk'), [WDL]));
Expand All @@ -165,11 +193,13 @@
nodes.push(wrap(rangeRow('baseLevel', 'Sessizlikte Işık', 0.02, 0.6, 0.01, true), [WDL, ORGB]));
nodes.push(wrap(rangeRow('spread', 'Renk Yayılımı', 0.1, 4, 0.05), [WDL, ORGB]));
}
nodes.push(wrap(rangeRow('updateRate', 'Güncelleme Hızı', 5, 60, 1), [WDL]));
nodes.push(el('div', {
class: 'lighting-mode-help',
text: 'OpenRGB kare hızı OpenRGB kartındaki Güncelleme Hızı kaydırıcısındadır.',
}));
if (wdlOn()) {
nodes.push(wrap(rangeRow('updateRate', 'Güncelleme Hızı', 5, 60, 1), [WDL]));
nodes.push(el('div', {
class: 'lighting-mode-help',
text: 'OpenRGB kare hızı OpenRGB kartındaki Güncelleme Hızı kaydırıcısındadır.',
}));
}
nodes.push(wrap(rangeRow('saturation', 'Renk Doygunluğu', 0, 1.5, 0.01), [WDL, ORGB]));
}

Expand Down
20 changes: 8 additions & 12 deletions src/admin/nowplaying-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@
if (!statusEl || !statusEl.isConnected) return;
const N = NP();
if (status && status.supported === false) {
statusEl.textContent = status.platform === 'win32'
? 'Sistemden okunamıyor' + (status.error ? ' — ' + status.error : '')
: 'Sistemden okuma yalnızca Windows’ta çalışıyor';
statusEl.textContent = 'Sistemden okunamıyor' + (status.error ? ' — ' + status.error : '');
statusEl.className = 'txt-info np-status bad';
return;
}
if ((!live || !live.has) && status && status.error) {
statusEl.textContent = 'Sistemden okunamıyor — ' + status.error;
statusEl.className = 'txt-info np-status bad';
return;
}
Expand Down Expand Up @@ -101,9 +104,7 @@
const nodes = [];

const isWin = !!(window.SV_PLATFORM && window.SV_PLATFORM.isWindows);
if (!isWin && (C.source || 'system') === 'system') {
C.source = 'manual';
}
if (!isWin && (C.source || 'system') === 'system') C.source = 'manual';
const src = C.source || (isWin ? 'system' : 'manual');
if (src === 'system' && isWin && window.api && window.api.nowPlayingSubscribe) {
window.api.nowPlayingSubscribe(true);
Expand All @@ -115,13 +116,8 @@

const sourceOpts = isWin ? SOURCE_LABELS : [['manual', 'Elle Yaz']];
nodes.push(SP().miniSelect('Kaynak', sourceOpts, () => src, (v) => {
if (!isWin && v === 'system') { C.source = 'manual'; }
else C.source = v;
C.source = isWin ? v : 'manual';
}, rerender));
if (!isWin) {
nodes.push(el('div', { class: 'studio-note dim-hint',
text: 'Sistemden okuma (SMTC) yalnızca Windows’ta çalışır. Bu platformda parçayı elle yazın.' }));
}

if (src === 'system') {
statusEl = el('span', { class: 'txt-info np-status', text: 'okunuyor…' });
Expand Down
33 changes: 9 additions & 24 deletions src/admin/scene-panels.js
Original file line number Diff line number Diff line change
Expand Up @@ -759,9 +759,7 @@
}
} else if (src === 'now') {
const isWin = isWindowsPlatform();
if (!isWin && txt.nowSource === 'system') {
txt.nowSource = 'manual';
}
if (!isWin && (txt.nowSource || 'system') === 'system') txt.nowSource = 'manual';
const isAuto = isWin && (txt.nowSource || 'system') === 'system';

out.push(miniSelect('Gösterilen Alan', [
Expand All @@ -770,18 +768,17 @@
['both', 'Parça ve Sanatçı'],
], () => txt.field || 'both', (v) => { txt.field = v; }, rerender));

if (isWin) {
if (isAuto && window.api && window.api.nowPlayingSubscribe) {
if (isWin && isAuto && window.api && window.api.nowPlayingSubscribe) {
window.api.nowPlayingSubscribe(true);
}
if (isWin) out.push(miniToggle('Sistemden Otomatik Doldur', () => isAuto, (v) => {
txt.nowSource = v ? 'system' : 'manual';
if (v && window.api && window.api.nowPlayingSubscribe) {
window.api.nowPlayingSubscribe(true);
}
out.push(miniToggle('Sistemden Otomatik Doldur', () => isAuto, (v) => {
txt.nowSource = v ? 'system' : 'manual';
if (v && window.api && window.api.nowPlayingSubscribe) {
window.api.nowPlayingSubscribe(true);
}
}, rerender));
}, rerender));

out.push(miniToggle('Şarkı Resmini Göster', () => txt.showArtwork !== false, (v) => {
if (isWin) out.push(miniToggle('Şarkı Resmini Göster', () => txt.showArtwork !== false, (v) => {
txt.showArtwork = v;
P().push(true);
}, rerender));
Expand Down Expand Up @@ -845,18 +842,6 @@
]));
}
}
} else {
// macOS / Linux
out.push(miniToggle('Sistemden Otomatik Doldur', () => false, () => {}, null, {
disabled: true,
badge: 'Yalnızca Windows',
title: 'Bu özellik şu anda yalnızca Windows (SMTC) üzerinde desteklenmektedir.',
}));
out.push(el('div', {
class: 'studio-note dim-hint',
text: 'Sistem medya oturumunu (SMTC) otomatik okuma şu anda yalnızca Windows’ta desteklenmektedir. Başlık ve sanatçı bilgilerini aşağıdan elle girebilirsiniz.',
}));
}

const titleLabel = isAuto ? 'Yedek Parça Adı' : 'Parça Adı';
const artistLabel = isAuto ? 'Yedek Sanatçı' : 'Sanatçı';
Expand Down
15 changes: 9 additions & 6 deletions src/admin/studio.css
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,9 @@ input.p-in[type='color'] { padding: 2px; height: 28px; width: 46px; flex: none;
.client-addr { color: var(--muted2); font: 10.5px 'Cascadia Code', 'Consolas', monospace; margin-left: auto; }

/* ========================= SAHNE ÜRETİCİ ========================= */
/* Button row (#579 / layout): flex:1 (basis 0%) forced equal columns in a
~300px card, so long TR/EN labels ("Kütüphaneye Kaydet" / "Save to Library")
overflowed and sat flush on the right edge. Wrap with a content-ish basis
and even gap; don't shrink below the label. Same idea as #622 add-row. */
/* Button row (#579): basis 0% squeezed long labels onto the right edge.
Size from the label, wrap onto the next line, and let a long English
label break inside the button instead of spilling past the card. */
.gen-actions {
display: flex;
flex-wrap: wrap;
Expand All @@ -196,9 +195,13 @@ input.p-in[type='color'] { padding: 2px; height: 28px; width: 46px; flex: none;
align-items: stretch;
}
.gen-actions .btn {
flex: 1 1 8.5rem;
min-width: min(100%, 8.5rem);
flex: 1 1 auto;
min-width: 0;
max-width: 100%;
white-space: normal;
overflow-wrap: anywhere;
text-align: center;
line-height: 1.25;
}
/* Mix history: icon-only chevrons stay compact; Save shares the rest without
margin-left:auto so disabled state does not jump to the far edge. */
Expand Down
Loading
Loading