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:
parent
0d21d8c8b3
commit
764644176c
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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');
|
||||
});
|
||||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue