@unbrained/pm-web 1.0.0
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.
- package/CHANGELOG.md +7 -0
- package/README.md +107 -0
- package/dist/auth.js +20 -0
- package/dist/auth.js.map +1 -0
- package/dist/crypto.js +42 -0
- package/dist/crypto.js.map +1 -0
- package/dist/db.js +111 -0
- package/dist/db.js.map +1 -0
- package/dist/index.js +88 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.js +16 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/routes/admin.js +207 -0
- package/dist/routes/admin.js.map +1 -0
- package/dist/routes/auth.js +163 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/github.js +354 -0
- package/dist/routes/github.js.map +1 -0
- package/dist/routes/groups.js +180 -0
- package/dist/routes/groups.js.map +1 -0
- package/dist/routes/pm.js +2446 -0
- package/dist/routes/pm.js.map +1 -0
- package/dist/routes/projects.js +151 -0
- package/dist/routes/projects.js.map +1 -0
- package/dist/routes/sharing.js +155 -0
- package/dist/routes/sharing.js.map +1 -0
- package/dist/server.js +64 -0
- package/dist/server.js.map +1 -0
- package/dist/services/pm-runner.js +190 -0
- package/dist/services/pm-runner.js.map +1 -0
- package/dist/services/sse.js +111 -0
- package/dist/services/sse.js.map +1 -0
- package/manifest.json +15 -0
- package/package.json +111 -0
- package/public/icons/icon-192.png +0 -0
- package/public/icons/icon-512.png +0 -0
- package/public/index.html +265 -0
- package/public/manifest.json +66 -0
- package/public/src/api.js +28 -0
- package/public/src/api.js.map +1 -0
- package/public/src/api.ts +29 -0
- package/public/src/app.js +926 -0
- package/public/src/app.js.map +1 -0
- package/public/src/app.ts +929 -0
- package/public/src/components/modals.js +62 -0
- package/public/src/components/modals.js.map +1 -0
- package/public/src/components/modals.ts +73 -0
- package/public/src/components/toast.js +10 -0
- package/public/src/components/toast.js.map +1 -0
- package/public/src/components/toast.ts +13 -0
- package/public/src/constants.js +30 -0
- package/public/src/constants.js.map +1 -0
- package/public/src/constants.ts +41 -0
- package/public/src/state.js +15 -0
- package/public/src/state.js.map +1 -0
- package/public/src/state.ts +19 -0
- package/public/src/types.js +5 -0
- package/public/src/types.js.map +1 -0
- package/public/src/types.ts +253 -0
- package/public/src/utils.js +57 -0
- package/public/src/utils.js.map +1 -0
- package/public/src/utils.ts +56 -0
- package/public/src/views/activity.js +47 -0
- package/public/src/views/activity.js.map +1 -0
- package/public/src/views/activity.ts +41 -0
- package/public/src/views/admin.js +435 -0
- package/public/src/views/admin.js.map +1 -0
- package/public/src/views/admin.ts +504 -0
- package/public/src/views/auth.js +81 -0
- package/public/src/views/auth.js.map +1 -0
- package/public/src/views/auth.ts +74 -0
- package/public/src/views/calendar.js +133 -0
- package/public/src/views/calendar.js.map +1 -0
- package/public/src/views/calendar.ts +129 -0
- package/public/src/views/comments-audit.js +109 -0
- package/public/src/views/comments-audit.js.map +1 -0
- package/public/src/views/comments-audit.ts +108 -0
- package/public/src/views/config.js +322 -0
- package/public/src/views/config.js.map +1 -0
- package/public/src/views/config.ts +344 -0
- package/public/src/views/context.js +98 -0
- package/public/src/views/context.js.map +1 -0
- package/public/src/views/context.ts +100 -0
- package/public/src/views/create.js +293 -0
- package/public/src/views/create.js.map +1 -0
- package/public/src/views/create.ts +246 -0
- package/public/src/views/dedupe.js +51 -0
- package/public/src/views/dedupe.js.map +1 -0
- package/public/src/views/dedupe.ts +43 -0
- package/public/src/views/export.js +300 -0
- package/public/src/views/export.js.map +1 -0
- package/public/src/views/export.ts +274 -0
- package/public/src/views/github.js +360 -0
- package/public/src/views/github.js.map +1 -0
- package/public/src/views/github.ts +308 -0
- package/public/src/views/graph-canvas.js +1986 -0
- package/public/src/views/graph-canvas.js.map +1 -0
- package/public/src/views/graph-canvas.ts +2218 -0
- package/public/src/views/graph.js +1824 -0
- package/public/src/views/graph.js.map +1 -0
- package/public/src/views/graph.ts +1891 -0
- package/public/src/views/groups.js +186 -0
- package/public/src/views/groups.js.map +1 -0
- package/public/src/views/groups.ts +172 -0
- package/public/src/views/guide.js +151 -0
- package/public/src/views/guide.js.map +1 -0
- package/public/src/views/guide.ts +162 -0
- package/public/src/views/health.js +105 -0
- package/public/src/views/health.js.map +1 -0
- package/public/src/views/health.ts +102 -0
- package/public/src/views/items.js +1306 -0
- package/public/src/views/items.js.map +1 -0
- package/public/src/views/items.ts +1196 -0
- package/public/src/views/normalize.js +67 -0
- package/public/src/views/normalize.js.map +1 -0
- package/public/src/views/normalize.ts +58 -0
- package/public/src/views/plan.js +454 -0
- package/public/src/views/plan.js.map +1 -0
- package/public/src/views/plan.ts +496 -0
- package/public/src/views/projects.js +204 -0
- package/public/src/views/projects.js.map +1 -0
- package/public/src/views/projects.ts +196 -0
- package/public/src/views/router.js +227 -0
- package/public/src/views/router.js.map +1 -0
- package/public/src/views/router.ts +188 -0
- package/public/src/views/search.js +103 -0
- package/public/src/views/search.js.map +1 -0
- package/public/src/views/search.ts +94 -0
- package/public/src/views/settings.js +272 -0
- package/public/src/views/settings.js.map +1 -0
- package/public/src/views/settings.ts +190 -0
- package/public/src/views/shared.js +49 -0
- package/public/src/views/shared.js.map +1 -0
- package/public/src/views/shared.ts +49 -0
- package/public/src/views/sharing.js +152 -0
- package/public/src/views/sharing.js.map +1 -0
- package/public/src/views/sharing.ts +139 -0
- package/public/src/views/stats.js +92 -0
- package/public/src/views/stats.js.map +1 -0
- package/public/src/views/stats.ts +88 -0
- package/public/src/views/templates.js +117 -0
- package/public/src/views/templates.js.map +1 -0
- package/public/src/views/templates.ts +113 -0
- package/public/src/views/validate.js +54 -0
- package/public/src/views/validate.js.map +1 -0
- package/public/src/views/validate.ts +48 -0
- package/public/styles.css +2231 -0
- package/public/sw.js +318 -0
- package/public/tsconfig.json +20 -0
- package/sql/schema.sql +105 -0
|
@@ -0,0 +1,265 @@
|
|
|
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, viewport-fit=cover, maximum-scale=1.0, user-scalable=no">
|
|
6
|
+
<meta name="theme-color" content="#0a0f1e">
|
|
7
|
+
<meta name="description" content="pm-web — browser UI for pm-cli project management">
|
|
8
|
+
<meta name="mobile-web-app-capable" content="yes">
|
|
9
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
10
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
11
|
+
<meta name="apple-mobile-web-app-title" content="pm-web">
|
|
12
|
+
<title>pm-web — Project Management</title>
|
|
13
|
+
<link rel="manifest" href="/manifest.json">
|
|
14
|
+
<link rel="apple-touch-icon" href="/icons/icon-192.png">
|
|
15
|
+
<link rel="apple-touch-icon" sizes="512x512" href="/icons/icon-512.png">
|
|
16
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
17
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
18
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
19
|
+
<link rel="stylesheet" href="/styles.css?v=19">
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<a href="#main-content" class="skip-link">Skip to main content</a>
|
|
23
|
+
<div id="app">
|
|
24
|
+
<!-- Auth Screen -->
|
|
25
|
+
<div id="auth-screen" style="display:none">
|
|
26
|
+
<div class="auth-left">
|
|
27
|
+
<div class="auth-brand">
|
|
28
|
+
<div class="auth-logo">pm<span>-web</span></div>
|
|
29
|
+
<div class="auth-tagline">Git-native project management</div>
|
|
30
|
+
<ul class="auth-features">
|
|
31
|
+
<li>Manage tasks, features, bugs, epics & more</li>
|
|
32
|
+
<li>Git-native storage in .agents/pm/</li>
|
|
33
|
+
<li>Full history, comments & activity feeds</li>
|
|
34
|
+
<li>Team-friendly project isolation</li>
|
|
35
|
+
</ul>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="auth-right">
|
|
39
|
+
<div class="auth-form-title" id="auth-title">Welcome back</div>
|
|
40
|
+
<div class="auth-form-sub" id="auth-sub">Sign in to your account to continue</div>
|
|
41
|
+
<div class="auth-toggle">
|
|
42
|
+
<button class="active" id="tab-login" onclick="window.__app.switchAuthTab('login')">Sign In</button>
|
|
43
|
+
<button id="tab-register" onclick="window.__app.switchAuthTab('register')">Register</button>
|
|
44
|
+
</div>
|
|
45
|
+
<form id="auth-form" onsubmit="window.__app.submitAuth(event)">
|
|
46
|
+
<div class="form-group" id="field-name" style="display:none">
|
|
47
|
+
<label class="form-label">Display Name</label>
|
|
48
|
+
<input class="form-input" type="text" id="auth-name" placeholder="Your name" autocomplete="name">
|
|
49
|
+
</div>
|
|
50
|
+
<div class="form-group">
|
|
51
|
+
<label class="form-label">Email</label>
|
|
52
|
+
<input class="form-input" type="email" id="auth-email" placeholder="you@example.com" required autocomplete="email">
|
|
53
|
+
</div>
|
|
54
|
+
<div class="form-group">
|
|
55
|
+
<label class="form-label">Password</label>
|
|
56
|
+
<input class="form-input" type="password" id="auth-password" placeholder="••••••••" required autocomplete="current-password">
|
|
57
|
+
</div>
|
|
58
|
+
<div class="form-error" id="auth-error" style="display:none"></div>
|
|
59
|
+
<button type="submit" class="btn btn-primary btn-full btn-lg" style="margin-top:8px" id="auth-submit">
|
|
60
|
+
<span id="auth-btn-text">Sign In</span>
|
|
61
|
+
</button>
|
|
62
|
+
</form>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<!-- Main App -->
|
|
67
|
+
<div id="main-app" style="display:none">
|
|
68
|
+
<nav id="top-nav" role="navigation" aria-label="Main navigation">
|
|
69
|
+
<div class="nav-logo">pm<span>-web</span></div>
|
|
70
|
+
<div class="nav-divider"></div>
|
|
71
|
+
<div class="project-selector-wrap">
|
|
72
|
+
<label for="project-selector" class="sr-only">Select project</label>
|
|
73
|
+
<select class="project-selector" id="project-selector" onchange="window.__app.onProjectSelect(this.value)" aria-label="Select project">
|
|
74
|
+
<option value="">— Select project —</option>
|
|
75
|
+
</select>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="nav-spacer"></div>
|
|
78
|
+
<div class="nav-search-hint" onclick="window.__app.openSearchModal()" title="Ctrl+K" role="button" tabindex="0" aria-label="Open search (Ctrl+K)" onkeydown="if(event.key==='Enter')window.__app.openSearchModal()">⌘K Search</div>
|
|
79
|
+
<div class="presence-bar" id="presence-bar" style="display:none" aria-label="Users viewing this project"></div>
|
|
80
|
+
<div class="sse-indicator" id="sse-indicator" title="Real-time sync disconnected">
|
|
81
|
+
<div class="sse-dot"></div>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="user-menu">
|
|
84
|
+
<div class="user-avatar" id="user-avatar" aria-hidden="true">?</div>
|
|
85
|
+
<span class="user-name" id="user-name-display"></span>
|
|
86
|
+
<button class="btn btn-ghost btn-sm" onclick="window.__app.showView('settings')" style="margin-right:4px" aria-label="Open settings">Settings</button>
|
|
87
|
+
<button class="btn btn-ghost btn-sm" onclick="window.__app.logout()" aria-label="Sign out">Sign out</button>
|
|
88
|
+
</div>
|
|
89
|
+
</nav>
|
|
90
|
+
<div id="app-body">
|
|
91
|
+
<nav id="sidebar" role="navigation" aria-label="Sidebar navigation">
|
|
92
|
+
<div class="sidebar-section" id="sidebar-projects-section">
|
|
93
|
+
<div class="sidebar-section-label">Projects</div>
|
|
94
|
+
<div class="sidebar-item active" onclick="window.__app.showView('projects')" data-view="projects" role="button" tabindex="0" aria-label="All Projects" onkeydown="if(event.key==='Enter')window.__app.showView('projects')">
|
|
95
|
+
<span class="sidebar-item-icon" aria-hidden="true">⊞</span> All Projects
|
|
96
|
+
</div>
|
|
97
|
+
<div class="sidebar-item" onclick="window.__app.showModal('create-project-modal')" style="color:var(--accent)" role="button" tabindex="0" aria-label="Create new project" onkeydown="if(event.key==='Enter')window.__app.showModal('create-project-modal')">
|
|
98
|
+
<span class="sidebar-item-icon" aria-hidden="true">+</span> New Project
|
|
99
|
+
</div>
|
|
100
|
+
<div class="sidebar-item" onclick="window.__app.showView('settings')" data-view="settings" role="button" tabindex="0" aria-label="Settings" onkeydown="if(event.key==='Enter')window.__app.showView('settings')">
|
|
101
|
+
<span class="sidebar-item-icon" aria-hidden="true">⚙</span> Settings
|
|
102
|
+
</div>
|
|
103
|
+
<div class="sidebar-item admin-only" onclick="window.__app.showView('admin')" data-view="admin" style="display:none" role="button" tabindex="0" aria-label="Admin panel" onkeydown="if(event.key==='Enter')window.__app.showView('admin')">
|
|
104
|
+
<span class="sidebar-item-icon" aria-hidden="true">◇</span> Admin
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="sidebar-section" id="sidebar-pm-section" style="display:none">
|
|
108
|
+
<div class="sidebar-section-label" id="sidebar-project-name">Project</div>
|
|
109
|
+
<div class="sidebar-item" onclick="window.__app.showView('items')" data-view="items" role="button" tabindex="0" aria-label="Items" onkeydown="if(event.key==='Enter')window.__app.showView('items')">
|
|
110
|
+
<span class="sidebar-item-icon" aria-hidden="true">≡</span> Items
|
|
111
|
+
<span class="sidebar-badge" id="badge-items"></span>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="sidebar-item" onclick="window.__app.showView('create')" data-view="create" role="button" tabindex="0" aria-label="Create item" onkeydown="if(event.key==='Enter')window.__app.showView('create')">
|
|
114
|
+
<span class="sidebar-item-icon" aria-hidden="true">+</span> Create Item
|
|
115
|
+
</div>
|
|
116
|
+
<div class="sidebar-item" onclick="window.__app.showView('plan')" data-view="plan" role="button" tabindex="0" aria-label="Plans" onkeydown="if(event.key==='Enter')window.__app.showView('plan')">
|
|
117
|
+
<span class="sidebar-item-icon" aria-hidden="true">◧</span> Plans
|
|
118
|
+
</div>
|
|
119
|
+
<div class="sidebar-item" onclick="window.__app.showView('activity')" data-view="activity" role="button" tabindex="0" aria-label="Activity" onkeydown="if(event.key==='Enter')window.__app.showView('activity')">
|
|
120
|
+
<span class="sidebar-item-icon" aria-hidden="true">◎</span> Activity
|
|
121
|
+
</div>
|
|
122
|
+
<div class="sidebar-item" onclick="window.__app.showView('search')" data-view="search" role="button" tabindex="0" aria-label="Search" onkeydown="if(event.key==='Enter')window.__app.showView('search')">
|
|
123
|
+
<span class="sidebar-item-icon" aria-hidden="true">⌕</span> Search
|
|
124
|
+
</div>
|
|
125
|
+
<div class="sidebar-item" onclick="window.__app.showView('stats')" data-view="stats" role="button" tabindex="0" aria-label="Stats" onkeydown="if(event.key==='Enter')window.__app.showView('stats')">
|
|
126
|
+
<span class="sidebar-item-icon" aria-hidden="true">◈</span> Stats
|
|
127
|
+
</div>
|
|
128
|
+
<div class="sidebar-item" onclick="window.__app.showView('calendar')" data-view="calendar" role="button" tabindex="0" aria-label="Calendar" onkeydown="if(event.key==='Enter')window.__app.showView('calendar')">
|
|
129
|
+
<span class="sidebar-item-icon" aria-hidden="true">◷</span> Calendar
|
|
130
|
+
</div>
|
|
131
|
+
<div class="sidebar-item" onclick="window.__app.showView('context')" data-view="context" role="button" tabindex="0" aria-label="Context" onkeydown="if(event.key==='Enter')window.__app.showView('context')">
|
|
132
|
+
<span class="sidebar-item-icon" aria-hidden="true">⚙</span> Context
|
|
133
|
+
</div>
|
|
134
|
+
<div class="sidebar-item" onclick="window.__app.showView('graph')" data-view="graph" role="button" tabindex="0" aria-label="Graph" onkeydown="if(event.key==='Enter')window.__app.showView('graph')">
|
|
135
|
+
<span class="sidebar-item-icon" aria-hidden="true">◎</span> Graph
|
|
136
|
+
</div>
|
|
137
|
+
<div class="sidebar-item" onclick="window.__app.showView('config')" data-view="config" role="button" tabindex="0" aria-label="Config" onkeydown="if(event.key==='Enter')window.__app.showView('config')">
|
|
138
|
+
<span class="sidebar-item-icon" aria-hidden="true">⚒</span> Config
|
|
139
|
+
</div>
|
|
140
|
+
<div class="sidebar-item" onclick="window.__app.showView('sharing')" data-view="sharing" role="button" tabindex="0" aria-label="Sharing" onkeydown="if(event.key==='Enter')window.__app.showView('sharing')">
|
|
141
|
+
<span class="sidebar-item-icon" aria-hidden="true">⇄</span> Sharing
|
|
142
|
+
</div>
|
|
143
|
+
<div class="sidebar-item" onclick="window.__app.showView('health')" data-view="health" role="button" tabindex="0" aria-label="Health" onkeydown="if(event.key==='Enter')window.__app.showView('health')">
|
|
144
|
+
<span class="sidebar-item-icon" aria-hidden="true">♥</span> Health
|
|
145
|
+
</div>
|
|
146
|
+
<div class="sidebar-item" onclick="window.__app.showView('dedupe')" data-view="dedupe" role="button" tabindex="0" aria-label="Dedupe audit" onkeydown="if(event.key==='Enter')window.__app.showView('dedupe')">
|
|
147
|
+
<span class="sidebar-item-icon" aria-hidden="true">⧖</span> Dedupe Audit
|
|
148
|
+
</div>
|
|
149
|
+
<div class="sidebar-item" onclick="window.__app.showView('validate')" data-view="validate" role="button" tabindex="0" aria-label="Validate" onkeydown="if(event.key==='Enter')window.__app.showView('validate')">
|
|
150
|
+
<span class="sidebar-item-icon" aria-hidden="true">✓</span> Validate
|
|
151
|
+
</div>
|
|
152
|
+
<div class="sidebar-item" onclick="window.__app.showView('github')" data-view="github" role="button" tabindex="0" aria-label="GitHub" onkeydown="if(event.key==='Enter')window.__app.showView('github')">
|
|
153
|
+
<span class="sidebar-item-icon" aria-hidden="true">⊙</span> GitHub
|
|
154
|
+
</div>
|
|
155
|
+
<div class="sidebar-item" onclick="window.__app.showView('export')" data-view="export" role="button" tabindex="0" aria-label="Export or import" onkeydown="if(event.key==='Enter')window.__app.showView('export')">
|
|
156
|
+
<span class="sidebar-item-icon" aria-hidden="true">↕</span> Export / Import
|
|
157
|
+
</div>
|
|
158
|
+
<div class="sidebar-item" onclick="window.__app.showView('normalize')" data-view="normalize" role="button" tabindex="0" aria-label="Normalize" onkeydown="if(event.key==='Enter')window.__app.showView('normalize')">
|
|
159
|
+
<span class="sidebar-item-icon" aria-hidden="true">⊞</span> Normalize
|
|
160
|
+
</div>
|
|
161
|
+
<div class="sidebar-item" onclick="window.__app.showView('templates')" data-view="templates" role="button" tabindex="0" aria-label="Templates" onkeydown="if(event.key==='Enter')window.__app.showView('templates')">
|
|
162
|
+
<span class="sidebar-item-icon" aria-hidden="true">⎘</span> Templates
|
|
163
|
+
</div>
|
|
164
|
+
<div class="sidebar-item" onclick="window.__app.showView('guide')" data-view="guide" role="button" tabindex="0" aria-label="Guide" onkeydown="if(event.key==='Enter')window.__app.showView('guide')">
|
|
165
|
+
<span class="sidebar-item-icon" aria-hidden="true">📖</span> Guide
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
<div class="sidebar-section" id="sidebar-logs-section">
|
|
169
|
+
<div class="sidebar-section-label">Logs</div>
|
|
170
|
+
<div class="sidebar-item" onclick="window.__app.showView('comments-audit')" data-view="comments-audit" role="button" tabindex="0" aria-label="Comments audit" onkeydown="if(event.key==='Enter')window.__app.showView('comments-audit')">
|
|
171
|
+
<span class="sidebar-item-icon" aria-hidden="true">💬</span> Comments Audit
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="sidebar-section">
|
|
175
|
+
<div class="sidebar-section-label">Team</div>
|
|
176
|
+
<div class="sidebar-item" onclick="window.__app.showView('groups')" data-view="groups" role="button" tabindex="0" aria-label="Groups" onkeydown="if(event.key==='Enter')window.__app.showView('groups')">
|
|
177
|
+
<span class="sidebar-item-icon" aria-hidden="true">◉</span> Groups
|
|
178
|
+
</div>
|
|
179
|
+
<div class="sidebar-item" onclick="window.__app.showView('shared')" data-view="shared" role="button" tabindex="0" aria-label="Shared with me" onkeydown="if(event.key==='Enter')window.__app.showView('shared')">
|
|
180
|
+
<span class="sidebar-item-icon" aria-hidden="true">⇄</span> Shared with Me
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</nav>
|
|
184
|
+
<main id="main-content" role="main">
|
|
185
|
+
<div class="content-area" id="content-projects"><!-- projects view --></div>
|
|
186
|
+
<div class="content-area" id="content-items" style="display:none"><!-- items --></div>
|
|
187
|
+
<div class="content-area" id="content-create" style="display:none"><!-- create --></div>
|
|
188
|
+
<div class="content-area" id="content-activity" style="display:none"><!-- activity --></div>
|
|
189
|
+
<div class="content-area" id="content-search" style="display:none"><!-- search --></div>
|
|
190
|
+
<div class="content-area" id="content-stats" style="display:none"><!-- stats --></div>
|
|
191
|
+
<div class="content-area" id="content-calendar" style="display:none"><!-- calendar --></div>
|
|
192
|
+
<div class="content-area" id="content-context" style="display:none"><!-- context --></div>
|
|
193
|
+
<div class="content-area" id="content-graph" style="display:none"><!-- graph --></div>
|
|
194
|
+
<div class="content-area" id="content-sharing" style="display:none"><!-- sharing --></div>
|
|
195
|
+
<div class="content-area" id="content-groups" style="display:none"><!-- groups --></div>
|
|
196
|
+
<div class="content-area" id="content-health" style="display:none"><!-- health --></div>
|
|
197
|
+
<div class="content-area" id="content-dedupe" style="display:none"><!-- dedupe --></div>
|
|
198
|
+
<div class="content-area" id="content-validate" style="display:none"><!-- validate --></div>
|
|
199
|
+
<div class="content-area" id="content-settings" style="display:none"><!-- settings --></div>
|
|
200
|
+
<div class="content-area" id="content-github" style="display:none"><!-- github --></div>
|
|
201
|
+
<div class="content-area" id="content-export" style="display:none"><!-- export/import --></div>
|
|
202
|
+
<div class="content-area" id="content-normalize" style="display:none"><!-- normalize --></div>
|
|
203
|
+
<div class="content-area" id="content-shared" style="display:none"><!-- shared with me --></div>
|
|
204
|
+
<div class="content-area" id="content-templates" style="display:none"><!-- templates --></div>
|
|
205
|
+
<div class="content-area" id="content-comments-audit" style="display:none"><!-- comments audit --></div>
|
|
206
|
+
<div class="content-area" id="content-config" style="display:none"><!-- project config --></div>
|
|
207
|
+
<div class="content-area" id="content-guide" style="display:none"><!-- guide --></div>
|
|
208
|
+
<div class="content-area" id="content-admin" style="display:none"><!-- admin --></div>
|
|
209
|
+
<div class="content-area" id="content-plan" style="display:none"><!-- plan --></div>
|
|
210
|
+
</main>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<!-- Modals -->
|
|
216
|
+
<div id="modal-container"></div>
|
|
217
|
+
<div id="toast-container"></div>
|
|
218
|
+
|
|
219
|
+
<!-- Offline Banner -->
|
|
220
|
+
<div class="offline-banner" id="offline-banner">
|
|
221
|
+
<span>You are offline — changes will sync when reconnected</span>
|
|
222
|
+
</div>
|
|
223
|
+
|
|
224
|
+
<!-- PWA Install Banner -->
|
|
225
|
+
<div class="install-banner" id="install-banner">
|
|
226
|
+
<div class="install-banner-text">Install <strong>pm-web</strong> for a native app experience</div>
|
|
227
|
+
<div style="display:flex;gap:8px">
|
|
228
|
+
<button class="btn btn-ghost btn-sm" onclick="dismissInstallBanner()">Not now</button>
|
|
229
|
+
<button class="btn btn-primary btn-sm" id="install-btn" onclick="installPwa()"><span>Install</span></button>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<!-- Mobile Bottom Nav (PWA) -->
|
|
234
|
+
<nav class="mobile-bottom-nav" id="mobile-bottom-nav">
|
|
235
|
+
<div class="mobile-bottom-nav-inner">
|
|
236
|
+
<div class="mobile-bottom-nav-item" onclick="window.__app.showView('items')" data-mobview="items" role="button" tabindex="0" aria-label="Items" onkeydown="if(event.key==='Enter')window.__app.showView('items')">
|
|
237
|
+
<div class="mobile-bottom-nav-item-icon" aria-hidden="true">≡</div>
|
|
238
|
+
<div class="mobile-bottom-nav-item-label">Items</div>
|
|
239
|
+
</div>
|
|
240
|
+
<div class="mobile-bottom-nav-item" onclick="window.__app.showView('create')" data-mobview="create" role="button" tabindex="0" aria-label="Create item" onkeydown="if(event.key==='Enter')window.__app.showView('create')">
|
|
241
|
+
<div class="mobile-bottom-nav-item-icon" aria-hidden="true">+</div>
|
|
242
|
+
<div class="mobile-bottom-nav-item-label">Create</div>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="mobile-bottom-nav-item" onclick="window.__app.openSearchModal()" data-mobview="search" role="button" tabindex="0" aria-label="Search" onkeydown="if(event.key==='Enter')window.__app.openSearchModal()">
|
|
245
|
+
<div class="mobile-bottom-nav-item-icon" aria-hidden="true">⌕</div>
|
|
246
|
+
<div class="mobile-bottom-nav-item-label">Search</div>
|
|
247
|
+
</div>
|
|
248
|
+
<div class="mobile-bottom-nav-item" onclick="window.__app.showView('stats')" data-mobview="stats" role="button" tabindex="0" aria-label="Stats" onkeydown="if(event.key==='Enter')window.__app.showView('stats')">
|
|
249
|
+
<div class="mobile-bottom-nav-item-icon" aria-hidden="true">◈</div>
|
|
250
|
+
<div class="mobile-bottom-nav-item-label">Stats</div>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="mobile-bottom-nav-item" onclick="window.__app.showView('shared')" data-mobview="shared" role="button" tabindex="0" aria-label="Shared" onkeydown="if(event.key==='Enter')window.__app.showView('shared')">
|
|
253
|
+
<div class="mobile-bottom-nav-item-icon" aria-hidden="true">⇄</div>
|
|
254
|
+
<div class="mobile-bottom-nav-item-label">Shared</div>
|
|
255
|
+
</div>
|
|
256
|
+
<div class="mobile-bottom-nav-item" onclick="window.__app.openMobileCommandSheet()" data-mobview="more" role="button" tabindex="0" aria-label="More options" onkeydown="if(event.key==='Enter')window.__app.openMobileCommandSheet()">
|
|
257
|
+
<div class="mobile-bottom-nav-item-icon" aria-hidden="true">⚙</div>
|
|
258
|
+
<div class="mobile-bottom-nav-item-label">More</div>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
</nav>
|
|
262
|
+
|
|
263
|
+
<script type="module" src="/src/app.js?v=9"></script>
|
|
264
|
+
</body>
|
|
265
|
+
</html>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pm-web — Project Management",
|
|
3
|
+
"short_name": "pm-web",
|
|
4
|
+
"description": "Full-featured web UI for pm-cli — manage projects, tasks, epics, bugs, and more from any device",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"background_color": "#0a0f1e",
|
|
8
|
+
"theme_color": "#2dd4bf",
|
|
9
|
+
"orientation": "any",
|
|
10
|
+
"scope": "/",
|
|
11
|
+
"lang": "en",
|
|
12
|
+
"dir": "ltr",
|
|
13
|
+
"categories": ["productivity", "utilities", "business"],
|
|
14
|
+
"icons": [
|
|
15
|
+
{
|
|
16
|
+
"src": "/icons/icon-192.png",
|
|
17
|
+
"sizes": "192x192",
|
|
18
|
+
"type": "image/png",
|
|
19
|
+
"purpose": "any"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"src": "/icons/icon-192.png",
|
|
23
|
+
"sizes": "192x192",
|
|
24
|
+
"type": "image/png",
|
|
25
|
+
"purpose": "maskable"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"src": "/icons/icon-512.png",
|
|
29
|
+
"sizes": "512x512",
|
|
30
|
+
"type": "image/png",
|
|
31
|
+
"purpose": "any"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"src": "/icons/icon-512.png",
|
|
35
|
+
"sizes": "512x512",
|
|
36
|
+
"type": "image/png",
|
|
37
|
+
"purpose": "maskable"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"shortcuts": [
|
|
41
|
+
{
|
|
42
|
+
"name": "New Item",
|
|
43
|
+
"url": "/?action=new-item",
|
|
44
|
+
"description": "Create a new pm item",
|
|
45
|
+
"icons": [{"src": "/icons/icon-192.png", "sizes": "192x192"}]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "Search",
|
|
49
|
+
"url": "/?action=search",
|
|
50
|
+
"description": "Search across all items",
|
|
51
|
+
"icons": [{"src": "/icons/icon-192.png", "sizes": "192x192"}]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"name": "New Project",
|
|
55
|
+
"url": "/?action=new-project",
|
|
56
|
+
"description": "Create a new pm project",
|
|
57
|
+
"icons": [{"src": "/icons/icon-192.png", "sizes": "192x192"}]
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"screenshots": [],
|
|
61
|
+
"prefer_related_applications": false,
|
|
62
|
+
"display_override": ["window-controls-overlay", "standalone", "minimal-ui"],
|
|
63
|
+
"edge_side_panel": {
|
|
64
|
+
"preferred_width": 400
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// API CLIENT
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5
|
+
export async function api(method, path, body) {
|
|
6
|
+
const opts = {
|
|
7
|
+
method,
|
|
8
|
+
headers: { 'Content-Type': 'application/json' },
|
|
9
|
+
credentials: 'include',
|
|
10
|
+
};
|
|
11
|
+
if (body !== undefined)
|
|
12
|
+
opts.body = JSON.stringify(body);
|
|
13
|
+
const res = await fetch('/api' + path, opts);
|
|
14
|
+
const data = await res.json().catch(() => ({}));
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
throw new Error(String(data.error || `HTTP ${res.status}`));
|
|
17
|
+
}
|
|
18
|
+
return data;
|
|
19
|
+
}
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
export async function getGuide(projectId) {
|
|
22
|
+
return api('GET', `/projects/${projectId}/pm/guide`);
|
|
23
|
+
}
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
export async function getGuideTopic(projectId, topicId) {
|
|
26
|
+
return api('GET', `/projects/${projectId}/pm/guide/${encodeURIComponent(topicId)}`);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["api.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,aAAa;AACb,kEAAkE;AAElE,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,MAAc,EAAE,IAAY,EAAE,IAAc;IACpE,MAAM,IAAI,GAAgB;QACxB,MAAM;QACN,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,WAAW,EAAE,SAAS;KACvB,CAAC;IACF,IAAI,IAAI,KAAK,SAAS;QAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAA4B,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,SAAiB;IAC9C,OAAO,GAAG,CAAC,KAAK,EAAE,aAAa,SAAS,WAAW,CAAC,CAAC;AACvD,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAiB,EAAE,OAAe;IACpE,OAAO,GAAG,CAAC,KAAK,EAAE,aAAa,SAAS,aAAa,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2
|
+
// API CLIENT
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
export async function api(method: string, path: string, body?: unknown): Promise<any> {
|
|
7
|
+
const opts: RequestInit = {
|
|
8
|
+
method,
|
|
9
|
+
headers: { 'Content-Type': 'application/json' },
|
|
10
|
+
credentials: 'include',
|
|
11
|
+
};
|
|
12
|
+
if (body !== undefined) opts.body = JSON.stringify(body);
|
|
13
|
+
const res = await fetch('/api' + path, opts);
|
|
14
|
+
const data = await res.json().catch((): Record<string, unknown> => ({}));
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
throw new Error(String(data.error || `HTTP ${res.status}`));
|
|
17
|
+
}
|
|
18
|
+
return data;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
export async function getGuide(projectId: string): Promise<any> {
|
|
23
|
+
return api('GET', `/projects/${projectId}/pm/guide`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
export async function getGuideTopic(projectId: string, topicId: string): Promise<any> {
|
|
28
|
+
return api('GET', `/projects/${projectId}/pm/guide/${encodeURIComponent(topicId)}`);
|
|
29
|
+
}
|