devglide 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" fill="#1c2128"/>
3
+ <rect width="32" height="32" fill="none" stroke="#7ee787" stroke-width="1.5"/>
4
+ <path d="M8 10h6a6 6 0 0 1 0 12H8V10z" fill="#7ee787"/>
5
+ <rect x="18" y="10" width="6" height="4" fill="#7ee787" opacity="0.6"/>
6
+ <rect x="18" y="18" width="6" height="4" fill="#7ee787" opacity="0.6"/>
7
+ </svg>
@@ -0,0 +1,78 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Devglide</title>
7
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
8
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/jetbrains-mono@5/index.min.css">
9
+ <link rel="stylesheet" href="/df/tokens.css">
10
+ <link rel="stylesheet" href="style.css">
11
+ <!-- Page module styles (scoped via .page-{name}) -->
12
+ <link rel="stylesheet" href="/app/kanban/page.css">
13
+ <link rel="stylesheet" href="/app/log/page.css">
14
+ <link rel="stylesheet" href="/app/test/page.css">
15
+ <link rel="stylesheet" href="/app/shell/page.css">
16
+ <link rel="stylesheet" href="/app/coder/page.css">
17
+ <link rel="stylesheet" href="/app/workflow/page.css">
18
+ <link rel="stylesheet" href="/app/voice/page.css">
19
+ <link rel="stylesheet" href="/app/vocabulary/page.css">
20
+ <link rel="stylesheet" href="/app/keymap/page.css">
21
+ <link rel="stylesheet" href="/app/prompts/page.css">
22
+ <link rel="stylesheet" href="/app/documentation/page.css">
23
+ <script src="/__devtools.js"></script>
24
+ </head>
25
+ <body>
26
+ <!-- Mobile top bar -->
27
+ <div class="mobile-topbar">
28
+ <button id="hamburger" class="hamburger" aria-label="Toggle menu">
29
+ <span></span><span></span><span></span>
30
+ </button>
31
+ <span id="mobile-title" class="mobile-title">Devglide</span>
32
+ </div>
33
+
34
+ <div class="layout">
35
+ <!-- Overlay for closing drawer on mobile -->
36
+ <div id="overlay" class="overlay"></div>
37
+
38
+ <aside id="sidebar" class="sidebar">
39
+ <div class="sidebar-brand">
40
+ <svg width="22" height="22" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" class="brand-icon">
41
+ <rect width="32" height="32" fill="var(--df-color-bg-base)"/>
42
+ <rect width="32" height="32" fill="none" stroke="var(--df-color-accent-default)" stroke-width="1.5"/>
43
+ <path d="M8 10h6a6 6 0 0 1 0 12H8V10z" fill="var(--df-color-accent-default)"/>
44
+ <rect x="18" y="10" width="6" height="4" fill="var(--df-color-accent-default)" opacity="0.6"/>
45
+ <rect x="18" y="18" width="6" height="4" fill="var(--df-color-accent-default)" opacity="0.6"/>
46
+ </svg>
47
+ <span class="brand-name">Devglide</span>
48
+ <span id="voice-widget-mount" class="sidebar-voice">
49
+ <div id="voice-error-popup" class="voice-error-popup hidden" role="alert" aria-live="assertive" aria-atomic="true"></div>
50
+ </span>
51
+ </div>
52
+
53
+ <div id="project-bar" class="project-bar">
54
+ <button id="project-selector" class="project-selector" aria-label="Select project" aria-haspopup="listbox">
55
+ <span id="project-name" class="project-name">No project</span>
56
+ <span class="project-chevron">&#9662;</span>
57
+ </button>
58
+ <div id="project-dropdown" class="project-dropdown" role="listbox" aria-label="Project list"></div>
59
+ </div>
60
+
61
+ <nav id="service-nav" class="service-nav" aria-label="Services"></nav>
62
+ </aside>
63
+
64
+ <main class="content">
65
+ <div id="app-content" class="app-content"></div>
66
+ </main>
67
+ </div>
68
+
69
+ <!-- Voice widget + keymap (regular scripts, set globals) -->
70
+ <script src="/shared-assets/voice-widget.js"></script>
71
+ <script src="/shared-assets/keymap-registry.js"></script>
72
+ <!-- Socket.io client (regular script, sets window.io) -->
73
+ <script src="/socket.io/socket.io.js"></script>
74
+ <!-- App shell (ES modules) -->
75
+ <script type="module" src="state.js"></script>
76
+ <script type="module" src="app.js"></script>
77
+ </body>
78
+ </html>
@@ -0,0 +1,84 @@
1
+ // ── Global State Module ───────────────────────────────────────────────────────
2
+ // Provides project context, socket connections, and API helpers.
3
+ // All page modules import from this.
4
+
5
+ // Socket.io connections (using window.io set by socket.io client script)
6
+ const io = window.io;
7
+
8
+ // Single socket on default namespace — dashboard (project:*) and shell
9
+ // (terminal:*/state:*/browser:*) events share it for backward compat.
10
+ export const dashboardSocket = io();
11
+ export const shellSocket = dashboardSocket;
12
+
13
+ // ── Reconnect handling ───────────────────────────────────────────────────────
14
+ // After a server restart, stale HTTP keep-alive connections cause the page to
15
+ // hang. Socket.io's reconnect also stalls on these dead connections, so the
16
+ // 'connect' event never fires. Instead, detect disconnect and reload after a
17
+ // short delay to give the new server time to start.
18
+ let _hasConnected = false;
19
+ let _reloadTimer = null;
20
+
21
+ dashboardSocket.on('connect', () => {
22
+ if (_reloadTimer) { clearTimeout(_reloadTimer); _reloadTimer = null; }
23
+ if (_hasConnected) {
24
+ // Reconnected after a disconnect — reload to clear stale state
25
+ window.location.reload();
26
+ }
27
+ _hasConnected = true;
28
+ });
29
+
30
+ dashboardSocket.on('disconnect', (reason) => {
31
+ if (!_hasConnected) return;
32
+ if (reason === 'io client disconnect') return; // intentional
33
+ // Server died — reload after delay (gives new server time to start)
34
+ _reloadTimer = setTimeout(() => window.location.reload(), 3000);
35
+ });
36
+
37
+ // ── Project context ───────────────────────────────────────────────────────────
38
+
39
+ export let activeProject = null;
40
+ let projectList = [];
41
+
42
+ const projectListeners = new Set();
43
+ const projectListListeners = new Set();
44
+
45
+ dashboardSocket.on('project:active', (project) => {
46
+ activeProject = project;
47
+ // Set cookie for backward compat with apps using cookie-based project context
48
+ if (project) {
49
+ document.cookie = `devglide-project-id=${project.id}; path=/; SameSite=Lax`;
50
+ } else {
51
+ document.cookie = 'devglide-project-id=; path=/; max-age=0';
52
+ }
53
+ for (const fn of projectListeners) fn(project);
54
+ });
55
+
56
+ dashboardSocket.on('project:list', (store) => {
57
+ projectList = store.projects || [];
58
+ for (const fn of projectListListeners) fn(projectList);
59
+ });
60
+
61
+ export function onProjectChange(fn) {
62
+ projectListeners.add(fn);
63
+ return () => projectListeners.delete(fn);
64
+ }
65
+
66
+ export function onProjectListChange(fn) {
67
+ projectListListeners.add(fn);
68
+ return () => projectListListeners.delete(fn);
69
+ }
70
+
71
+ export function getProjectList() { return projectList; }
72
+ export function getActiveProject() { return activeProject; }
73
+
74
+ // ── API helper ────────────────────────────────────────────────────────────────
75
+
76
+ export function api(path, options = {}) {
77
+ return fetch(path, {
78
+ ...options,
79
+ headers: {
80
+ 'Content-Type': 'application/json',
81
+ ...options.headers,
82
+ },
83
+ });
84
+ }