diff --git a/public/studio/Column.js b/public/studio/Column.js
index a582925..e16bc3f 100644
--- a/public/studio/Column.js
+++ b/public/studio/Column.js
@@ -65,7 +65,7 @@ export default class Column {
}
const btn = document.createElement('button');
btn.className = `mwse-btn ${className ?? ''}`;
- btn.textContent = label;
+ btn.innerHTML = label; // HTML destekler (ikonlu etiketler için)
btn.addEventListener('click', onClick);
this._actionsEl.appendChild(btn);
}
@@ -96,7 +96,12 @@ export default class Column {
const icon = document.createElement('span');
icon.className = 'mwse-item__icon';
- icon.textContent = item.icon ?? '○';
+ // Material Icons: icon adı string ise text, HTML içeriyorsa innerHTML
+ if (item.icon?.includes('<')) {
+ icon.innerHTML = item.icon;
+ } else {
+ icon.textContent = item.icon ?? 'circle';
+ }
row.appendChild(icon);
const body = document.createElement('div');
@@ -120,7 +125,7 @@ export default class Column {
if (item.hasChildren !== false) {
const arrow = document.createElement('span');
arrow.className = 'mwse-item__arrow';
- arrow.textContent = '›';
+ arrow.textContent = 'chevron_right';
row.appendChild(arrow);
}
diff --git a/public/studio/Studio.js b/public/studio/Studio.js
index 40a5608..9af5359 100644
--- a/public/studio/Studio.js
+++ b/public/studio/Studio.js
@@ -1,19 +1,22 @@
// MWSE Studio — dahili yönetim arayüzü.
-// Akış: bağlan → ID'yi kopyala → eşle → WebRTC ara / oda oluştur
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'
+ this.mwse = mwse;
+ this._el = typeof container === 'string'
? document.querySelector(container) : container;
- this._view = new ColumnView(this._el);
- this._devices = { cameras: [], microphones: [] };
- this._statusEl = null;
+ this._view = null; // mount() içinde oluşturulur
+ this._devices = { cameras: [], microphones: [] };
+ this._statusEl = null;
this._notifArea = null;
- this._styleOK = false;
- this._remoteMedia = [];
+ this._myIPEl = null;
+ this._myUUIDEl = null;
+ this._localGrid = null; // "Gönderiyorum" tile grid'i
+ this._remoteGrid = null; // "Geliyor" tile grid'i
+ this._streamsPanel = null;
+ this._styleOK = false;
}
async mount() {
@@ -21,30 +24,36 @@ export default class 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();
- // Scope sonrası sanal IP al ve kendime ata.
+ // Sanal IP al
this.mwse.scope(async () => {
try {
const ip = await this.mwse.virtualPressure.allocAPIPAddress();
- // PeerInfo.set → auth/info → mevcut tüm pairlere yayımlar.
this.mwse.me.info.set('ip', ip);
- this._updateMyIP(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);
- });
+ // 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)
+ // Eşleşme onaylandı
this.mwse.me.on('accepted/pair', peer => {
this._watchIncoming(peer);
- // Yeni paire kendi IP'mizi paylaş.
if (this.mwse.virtualPressure.APIPAddress) {
this.mwse.me.info.set('ip', this.mwse.virtualPressure.APIPAddress);
}
@@ -52,91 +61,201 @@ export default class Studio {
this._setStatus('online', `${this._peerLabel(peer)} eşleşmesi kuruldu`);
});
- // Pair info değişince (karşı tarafın IP'si gelince) kolonları yenile.
- this.mwse.me.on('accepted/pair', () => this._view.refresh());
- this.mwse.on('room', () => this._view.refresh());
this.mwse.me.on('end/pair', () => this._view.refresh());
+ this.mwse.on('room', () => this._view.refresh());
return this;
}
- // Toolbar'daki IP/ID kartını güncelle.
- _updateMyIP(ip) {
- if (this._myIPEl) this._myIPEl.textContent = ip;
- }
-
- // Bir peer için görünen ad: sanal IP varsa onu, yoksa kısa UUID.
- _peerLabel(peer) {
- return peer.info?.info?.ip || peer.socketId.slice(-8);
- }
-
// ── Araç çubuğu ──────────────────────────────────────────────────────────
_buildToolbar() {
const bar = document.createElement('div');
bar.className = 'mwse-studio__toolbar';
- // Logo
const logo = document.createElement('span');
logo.className = 'mwse-studio__title';
logo.innerHTML = 'MWSE Studio';
bar.appendChild(logo);
- // Benim IP + ID kartı (tıkla → UUID kopyalanır)
+ // ID kartı: sadece IP + kısa UUID + kopyala
const idCard = document.createElement('div');
idCard.className = 'mwse-id-card';
- idCard.title = 'Socket ID\'yi kopyala (paylaşmak için)';
+ idCard.title = 'Socket ID kopyala (bağlantı paylaşımı için)';
- const idLabel = document.createElement('span');
- idLabel.className = 'mwse-id-card__label';
- idLabel.textContent = 'Kimliğim';
-
- // Sanal IP (önce boş, allocAPIPAddress sonrası dolar)
this._myIPEl = document.createElement('span');
this._myIPEl.className = 'mwse-id-card__ip';
- this._myIPEl.textContent = '';
+ this._myIPEl.textContent = '●'; // IP gelmeden önce nokta
- // Kısa UUID (ID paylaşımı için)
- const idValue = document.createElement('span');
- idValue.className = 'mwse-id-card__value';
- idValue.textContent = '…';
+ this._myUUIDEl = document.createElement('span');
+ this._myUUIDEl.className = 'mwse-id-card__value';
+ this._myUUIDEl.textContent = '…';
- const idCopy = document.createElement('span');
- idCopy.className = 'mwse-id-card__copy';
- idCopy.textContent = '⎘';
+ const copyIcon = document.createElement('span');
+ copyIcon.className = 'mwse-id-card__copy';
+ copyIcon.textContent = '⎘';
- idCard.append(idLabel, this._myIPEl, idValue, idCopy);
+ idCard.append(this._myIPEl, this._myUUIDEl, copyIcon);
idCard.addEventListener('click', () => {
const id = this.mwse.me.socketId;
- if (!id || id === '…') return;
+ if (!id) return;
navigator.clipboard.writeText(id).then(() => {
- idCopy.textContent = '✓';
+ copyIcon.textContent = '✓';
idCard.classList.add('mwse-id-card--copied');
setTimeout(() => {
idCard.classList.remove('mwse-id-card--copied');
- idCopy.textContent = '⎘';
+ copyIcon.textContent = '⎘';
}, 2000);
});
});
-
- this.mwse.me.on('scope', () => { idValue.textContent = this.mwse.me.socketId.slice(-8); });
bar.appendChild(idCard);
- // Durum
this._statusEl = document.createElement('span');
this._statusEl.className = 'mwse-studio__status mwse-studio__status--online';
- this._statusEl.textContent = 'Bağlı';
+ 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'ı ekle
+ _addRemoteTile(track, peerLabel) {
+ const isVideo = track.kind === 'video';
+ const stream = new MediaStream([track]);
+ const tile = this._makeTile(
+ isVideo ? 'Video' : 'Ses',
+ `← ${peerLabel}`,
+ stream,
+ isVideo,
+ !isVideo, // ses tile'ı için muted değil (çalması lazım)
+ () => tile.remove() && this._updatePanelVisibility()
+ );
+ // Ses için görünmez