Studio ürünü sdk/'dan public/studio/'a taşındı + /studio route

sdk/studio/ → public/studio/:
  - index.js      → Studio.js (import yolları güncellendi:
                     ../webrtc/ → /sdk/webrtc/, ./ColumnView → /studio/ColumnView)
  - ColumnView.js → /studio/ altında (import: ./Column → /studio/Column)
  - Column.js, style.css → taşındı, değişmedi

public/studio/index.html — tam ürün HTML'i:
  - Loading overlay (spinner, hata durumu)
  - <script type="module" src="/studio/app.js">

public/studio/app.js — uygulama başlangıcı:
  - MWSE /sdk/index.js'den import
  - Studio /studio/Studio.js'den import
  - scope/close/error olaylarına göre loading overlay yönetimi

httpserver.go:
  - /studio     → public/studio/index.html
  - /studio/    → http.FileServer(public/studio/) — JS/CSS asset'leri

localhost:PORT/studio → Studio açılıyor
go test -race ./... — tüm testler yeşil

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
abdussamedulutas 2026-06-17 13:13:56 +03:00
parent 0d21d8c8b3
commit 764644176c
7 changed files with 90 additions and 10 deletions

View File

@ -65,13 +65,17 @@ func New(hub *ws.Hub, cfg config.Config, srvOpts ...ServerOptions) *http.Server
// registerStatic wires the asset routes:
//
// - /sdk.js -> 301 /sdk/index.js (import.meta.url resolves correctly for relative imports)
// - /sdk/ -> ES-module SDK files served from cfg.SDKDir
// - / -> /sdk/index.js redirect (bare URL returns the SDK entry)
// - /<file> -> matching file under cfg.PublicDir
// - anything -> public/status.xml fallback
// - /sdk.js -> 301 /sdk/index.js (import.meta.url resolves correctly)
// - /sdk/ -> ES-module SDK files served from cfg.SDKDir
// - /studio -> Studio product HTML (built-in UI)
// - /studio/ -> Studio JS/CSS assets under public/studio/
// - / -> /sdk/index.js redirect (bare URL returns the SDK entry)
// - /<file> -> matching file under cfg.PublicDir
// - anything -> public/status.xml fallback
func registerStatic(mux *http.ServeMux, cfg config.Config) {
statusDoc := filepath.Join(cfg.PublicDir, "status.xml")
statusDoc := filepath.Join(cfg.PublicDir, "status.xml")
studioDir := filepath.Join(cfg.PublicDir, "studio")
studioHTML := filepath.Join(studioDir, "index.html")
// /sdk.js → /sdk/index.js: keeps import.meta.url = /sdk/index.js so that
// ./EventTarget.js etc. resolve to /sdk/EventTarget.js (same origin).
@ -80,6 +84,13 @@ func registerStatic(mux *http.ServeMux, cfg config.Config) {
})
mux.Handle("/sdk/", http.StripPrefix("/sdk/", http.FileServer(http.Dir(cfg.SDKDir))))
// /studio → Studio product (built-in management UI).
// /studio/ → JS/CSS assets (ColumnView.js, Studio.js, style.css, …).
mux.HandleFunc("/studio", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, studioHTML)
})
mux.Handle("/studio/", http.StripPrefix("/studio/", http.FileServer(http.Dir(studioDir))))
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
http.Redirect(w, r, "/sdk/index.js", http.StatusFound)

View File

@ -1,7 +1,7 @@
// Miller-column view: manages a horizontal row of up to 5 columns.
// Selecting an item in column N removes columns N+1…end and adds the
// new column opened by that item's onSelect callback.
import Column from './Column.js';
import Column from '/studio/Column.js';
export default class ColumnView {
constructor(container) {

View File

@ -3,14 +3,14 @@
// Hierarchy: Network → Groups → Peers → Devices → Streams → Quality
//
// Usage:
// import Studio from '/sdk/studio/index.js';
// import Studio from '/studio/Studio.js';
// const studio = new Studio(mwse, '#app');
// studio.mount();
//
// The Studio is purely additive: it renders into the given container and
// never modifies the MWSE SDK state except through the public SDK API.
import ColumnView from './ColumnView.js';
import { MediaSources } from '../webrtc/index.js';
import ColumnView from '/studio/ColumnView.js';
import { MediaSources } from '/sdk/webrtc/index.js';
export default class Studio {
constructor(mwse, container) {

23
public/studio/app.js Normal file
View File

@ -0,0 +1,23 @@
import MWSE from '/sdk/index.js';
import Studio from '/studio/Studio.js';
const mwse = new MWSE();
const studio = new Studio(mwse, document.getElementById('app'));
const loadingEl = document.getElementById('loading');
const loadingMsg = document.getElementById('loading-msg');
mwse.on('scope', () => {
loadingEl.classList.add('hidden');
studio.mount();
});
mwse.on('close', () => {
loadingMsg.textContent = 'Bağlantı kesildi — yeniden bağlanılıyor…';
loadingEl.classList.remove('hidden');
});
mwse.on('error', err => {
loadingMsg.textContent = `Hata: ${err.message}`;
loadingEl.classList.remove('hidden');
});

46
public/studio/index.html Normal file
View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MWSE Studio</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #1a1a1a; }
#app { width: 100%; height: 100%; display: flex; flex-direction: column; }
/* Loading overlay */
#loading {
position: fixed; inset: 0; background: #1a1a1a;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
color: #888; font-family: 'Segoe UI', system-ui, sans-serif;
font-size: 13px; gap: 14px; z-index: 9999;
}
#loading .logo {
font-size: 22px; font-weight: 700; color: #fff;
letter-spacing: .12em; text-transform: uppercase;
}
#loading .logo span { color: #0078d4; }
#loading .spinner {
width: 22px; height: 22px;
border: 2px solid #2a2a2a;
border-top-color: #0078d4;
border-radius: 50%;
animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
#loading.hidden { display: none; }
</style>
</head>
<body>
<div id="loading">
<div class="logo">MWSE <span>Studio</span></div>
<div class="spinner"></div>
<div id="loading-msg">Sunucuya bağlanıyor…</div>
</div>
<div id="app"></div>
<script type="module" src="/studio/app.js"></script>
</body>
</html>