// MWSE Studio — dahili yönetim arayüzü.
import ColumnView from '/studio/ColumnView.js';
import { MediaSources } from '/sdk/webrtc/index.js';
export default class Studio {
constructor(mwse, container) {
this.mwse = mwse;
this._el = typeof container === 'string'
? document.querySelector(container) : container;
this._view = null;
this._devices = { cameras: [], microphones: [] };
this._statusEl = null;
this._notifArea = null;
this._myIPEl = null;
this._myUUIDEl = null;
this._localGrid = null;
this._remoteGrid = null;
this._streamsPanel = null;
this._styleOK = false;
this._peersCol = null; // reaktif güncelleme için referans
}
async mount() {
this._el.classList.add('mwse-studio');
this._injectStyle();
this._buildToolbar();
this._buildNotifArea();
// Ana alan: kolonlar (sol) + akış monitörü (sağ)
const mainArea = document.createElement('div');
mainArea.className = 'mwse-studio__main';
this._el.appendChild(mainArea);
this._view = new ColumnView(mainArea);
this._view.mount();
this._streamsPanel = this._buildStreamsPanel(mainArea);
await this._loadDevices();
this._pushRootColumn();
// Sanal IP al
this.mwse.scope(async () => {
try {
const ip = await this.mwse.virtualPressure.allocAPIPAddress();
this.mwse.me.info.set('ip', ip);
if (this._myIPEl) this._myIPEl.textContent = ip;
if (this._myUUIDEl) this._myUUIDEl.textContent = this.mwse.me.socketId.slice(-8);
} catch (_) {}
});
// Gelen eşleme isteği → bildirim banner
this.mwse.me.on('request/pair', peer => this._showPairRequest(peer));
// Eşleşme onaylandı (istek gönderen taraf)
this.mwse.me.on('accepted/pair', peer => {
this._watchIncoming(peer);
if (this.mwse.virtualPressure.APIPAddress) {
this.mwse.me.info.set('ip', this.mwse.virtualPressure.APIPAddress);
}
this._rebuildPeerItems();
this._setStatus('online', `${this._peerLabel(peer)} eşleşmesi kuruldu`);
});
// Eşleşme bitti (karşı taraf koptu veya endPair çağrıldı)
this.mwse.me.on('end/pair', (from) => {
this._rebuildPeerItems();
this._setStatus('', `${typeof from === 'string' ? from.slice(-8) : ''} ayrıldı`);
});
// WebSocket bağlantısı kopunca (sayfa yenileme, ağ kesilmesi)
this.mwse.me.on('peer/disconnect', peer => {
this._rebuildPeerItems();
this._setStatus('error', `${this._peerLabel(peer)} bağlantısı kesildi`);
});
this.mwse.on('room', () => this._view.refresh());
return this;
}
// ── Araç çubuğu ──────────────────────────────────────────────────────────
_buildToolbar() {
const bar = document.createElement('div');
bar.className = 'mwse-studio__toolbar';
const logo = document.createElement('span');
logo.className = 'mwse-studio__title';
logo.innerHTML = 'MWSE Studio';
bar.appendChild(logo);
// ID kartı: sadece IP + kısa UUID + kopyala
const idCard = document.createElement('div');
idCard.className = 'mwse-id-card';
idCard.title = 'Socket ID kopyala (bağlantı paylaşımı için)';
this._myIPEl = document.createElement('span');
this._myIPEl.className = 'mwse-id-card__ip';
this._myIPEl.textContent = '●'; // IP gelmeden önce nokta
this._myUUIDEl = document.createElement('span');
this._myUUIDEl.className = 'mwse-id-card__value';
this._myUUIDEl.textContent = '…';
const copyIcon = document.createElement('span');
copyIcon.className = 'mwse-id-card__copy';
copyIcon.textContent = '⎘';
idCard.append(this._myIPEl, this._myUUIDEl, copyIcon);
idCard.addEventListener('click', () => {
const id = this.mwse.me.socketId;
if (!id) return;
navigator.clipboard.writeText(id).then(() => {
copyIcon.textContent = '✓';
idCard.classList.add('mwse-id-card--copied');
setTimeout(() => {
idCard.classList.remove('mwse-id-card--copied');
copyIcon.textContent = '⎘';
}, 2000);
});
});
bar.appendChild(idCard);
this._statusEl = document.createElement('span');
this._statusEl.className = 'mwse-studio__status mwse-studio__status--online';
this._statusEl.innerHTML = 'wifi Bağlı';
bar.appendChild(this._statusEl);
// Canlı saat
const clock = document.createElement('span');
clock.className = 'mwse-studio__clock';
const tick = () => {
const now = new Date();
clock.textContent = now.toTimeString().slice(0, 8);
};
tick(); setInterval(tick, 1000);
bar.appendChild(clock);
this._el.insertBefore(bar, this._el.firstChild);
}
// ── Akış monitör paneli ───────────────────────────────────────────────────
_buildStreamsPanel(parent) {
const panel = document.createElement('div');
panel.className = 'mwse-streams-panel';
panel.style.display = 'none'; // akış yokken gizli
// Gönderiyorum bölümü
const outSec = document.createElement('div');
outSec.className = 'mwse-streams-section';
const outTitle = document.createElement('div');
outTitle.className = 'mwse-streams-section__title';
outTitle.textContent = '▲ Gönderiyorum';
this._localGrid = document.createElement('div');
this._localGrid.className = 'mwse-streams-grid';
outSec.append(outTitle, this._localGrid);
// Geliyor bölümü
const inSec = document.createElement('div');
inSec.className = 'mwse-streams-section';
const inTitle = document.createElement('div');
inTitle.className = 'mwse-streams-section__title';
inTitle.textContent = '▼ Geliyor';
this._remoteGrid = document.createElement('div');
this._remoteGrid.className = 'mwse-streams-grid';
inSec.append(inTitle, this._remoteGrid);
panel.append(outSec, inSec);
parent.appendChild(panel);
return panel;
}
// Yerel (gönderilen) akış tile'ı ekle
_addLocalTile(label, stream, peerLabel) {
const hasVideo = stream.getVideoTracks().length > 0;
const tile = this._makeTile(
label,
`→ ${peerLabel}`,
stream,
hasVideo,
true, // muted (geri besleme yok)
() => tile.remove() && this._updatePanelVisibility()
);
this._localGrid.appendChild(tile);
this._updatePanelVisibility();
}
// Uzak (gelen) track tile'ı → ses kontrollü
_addRemoteTile(track, streams, peerLabel) {
const isVideo = track.kind === 'video';
const stream = streams?.[0] ?? new MediaStream([track]);
const tile = document.createElement('div');
tile.className = `mwse-stream-tile${isVideo ? '' : ' mwse-stream-tile--audio'}`;
// Media element
let mediaEl;
if (isVideo) {
mediaEl = document.createElement('video');
mediaEl.autoplay = true;
mediaEl.playsInline = true;
mediaEl.muted = false;
mediaEl.srcObject = stream;
mediaEl.className = 'mwse-stream-tile__video';
tile.appendChild(mediaEl);
} else {
// Ses: görünür tile + gizli