hotsheet 0.1.2 → 0.2.1
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/README.md +71 -7
- package/dist/cli.js +173 -25
- package/dist/client/app.global.js +3 -3
- package/dist/client/styles.css +1 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -12,15 +12,32 @@ No cloud. No logins. No JIRA. Just tickets and a tight feedback loop.
|
|
|
12
12
|
|
|
13
13
|
<br>
|
|
14
14
|
|
|
15
|
+
**Desktop app** (recommended) — download from [GitHub Releases](https://github.com/brianwestphal/hotsheet/releases):
|
|
16
|
+
|
|
17
|
+
| Platform | Download |
|
|
18
|
+
|----------|----------|
|
|
19
|
+
| macOS (Apple Silicon) | `.dmg` (arm64) |
|
|
20
|
+
| macOS (Intel) | `.dmg` (x64) |
|
|
21
|
+
| Linux | `.AppImage` / `.deb` |
|
|
22
|
+
| Windows | `.msi` / `.exe` |
|
|
23
|
+
|
|
24
|
+
After installing, open the app and click **Install CLI** to add the `hotsheet` command to your PATH.
|
|
25
|
+
|
|
26
|
+
**Or install via npm:**
|
|
27
|
+
|
|
15
28
|
```bash
|
|
16
29
|
npm install -g hotsheet
|
|
17
30
|
```
|
|
18
31
|
|
|
32
|
+
Then, from any project directory:
|
|
33
|
+
|
|
19
34
|
```bash
|
|
20
35
|
hotsheet
|
|
21
36
|
```
|
|
22
37
|
|
|
23
|
-
That's it.
|
|
38
|
+
That's it. Data stays local.
|
|
39
|
+
|
|
40
|
+
> **Note:** We're actively developing and testing on macOS. Linux and Windows builds are provided but less tested — if you run into issues on those platforms, we'd love your help! Please [open an issue](https://github.com/brianwestphal/hotsheet/issues).
|
|
24
41
|
|
|
25
42
|
</div>
|
|
26
43
|
|
|
@@ -63,13 +80,19 @@ The loop stays tight because the AI always knows what to work on next.
|
|
|
63
80
|
<img src="docs/demo-3.png" alt="Sidebar filtering by Bug category" width="900">
|
|
64
81
|
</p>
|
|
65
82
|
|
|
66
|
-
**
|
|
83
|
+
**Column view** — switch to a kanban-style board grouped by status. Drag tickets between columns to change status, or drag onto sidebar items to set category, priority, or view.
|
|
84
|
+
|
|
85
|
+
<p align="center">
|
|
86
|
+
<img src="docs/demo-7.png" alt="Column view showing tickets organized by status in a kanban board" width="900">
|
|
87
|
+
</p>
|
|
88
|
+
|
|
89
|
+
**Batch operations** — select multiple tickets to bulk-update category, priority, status, or Up Next. Multi-select works in both list and column views.
|
|
67
90
|
|
|
68
91
|
<p align="center">
|
|
69
92
|
<img src="docs/demo-5.png" alt="Multiple tickets selected with the batch toolbar visible" width="900">
|
|
70
93
|
</p>
|
|
71
94
|
|
|
72
|
-
**Detail panel** — side or bottom orientation, resizable, with fields for title, details, attachments, and timestamped notes. Auto-shows when you select a ticket.
|
|
95
|
+
**Detail panel** — side or bottom orientation (toggle in the toolbar), resizable, with fields for title, details, attachments, and timestamped notes. Auto-shows when you select a ticket.
|
|
73
96
|
|
|
74
97
|
<p align="center">
|
|
75
98
|
<img src="docs/demo-6.png" alt="Detail panel in bottom orientation showing ticket details and notes" width="900">
|
|
@@ -78,12 +101,13 @@ The loop stays tight because the AI always knows what to work on next.
|
|
|
78
101
|
**Also includes:**
|
|
79
102
|
- **Five priority levels** — Highest to Lowest, sortable and filterable
|
|
80
103
|
- **Up Next flag** — star tickets to add them to the AI worklist
|
|
104
|
+
- **Drag and drop** — drag tickets onto sidebar views to change category, priority, or status
|
|
81
105
|
- **Search** — full-text search across ticket titles and details
|
|
82
|
-
- **Keyboard-driven** — `Enter` to create, `Cmd+I/B/F/R/K/G` for categories, `Alt+1-5` for priority, `Cmd+D` for Up Next
|
|
106
|
+
- **Keyboard-driven** — `Enter` to create, `Cmd+I/B/F/R/K/G` for categories, `Alt+1-5` for priority, `Cmd+D` for Up Next, `Cmd+C` to copy
|
|
107
|
+
- **Copy for commits** — `Cmd+C` copies selected ticket info (number + title) for use in commit messages
|
|
83
108
|
- **File attachments** — attach files to any ticket
|
|
84
109
|
- **Markdown sync** — `worklist.md` and `open-tickets.md` auto-generated on every change
|
|
85
110
|
- **Auto-cleanup** — configurable auto-deletion of old trash and verified items
|
|
86
|
-
- **Settings** — detail panel position, cleanup intervals, all persisted
|
|
87
111
|
- **Fully local** — embedded PostgreSQL (PGLite), no network calls, no accounts, no telemetry
|
|
88
112
|
|
|
89
113
|
---
|
|
@@ -147,6 +171,28 @@ TICKET HS-15:
|
|
|
147
171
|
|
|
148
172
|
## Install
|
|
149
173
|
|
|
174
|
+
### Desktop app (recommended)
|
|
175
|
+
|
|
176
|
+
Download the latest release for your platform from [GitHub Releases](https://github.com/brianwestphal/hotsheet/releases).
|
|
177
|
+
|
|
178
|
+
On first launch, the app will prompt you to install the `hotsheet` CLI command. This creates a symlink so you can launch the desktop app from any project directory. You can also install it manually:
|
|
179
|
+
|
|
180
|
+
**macOS:**
|
|
181
|
+
```bash
|
|
182
|
+
sudo ln -sf "/Applications/Hot Sheet.app/Contents/Resources/resources/hotsheet" /usr/local/bin/hotsheet
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Linux:**
|
|
186
|
+
```bash
|
|
187
|
+
ln -sf /path/to/hotsheet/resources/hotsheet-linux ~/.local/bin/hotsheet
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The desktop app includes automatic updates — new versions are downloaded and applied in the background.
|
|
191
|
+
|
|
192
|
+
### npm
|
|
193
|
+
|
|
194
|
+
Alternatively, install via npm (runs in your browser instead of a native window):
|
|
195
|
+
|
|
150
196
|
```bash
|
|
151
197
|
npm install -g hotsheet
|
|
152
198
|
```
|
|
@@ -158,14 +204,17 @@ Requires **Node.js 20+**.
|
|
|
158
204
|
## Usage
|
|
159
205
|
|
|
160
206
|
```bash
|
|
161
|
-
# Start
|
|
207
|
+
# Start from your project directory
|
|
162
208
|
hotsheet
|
|
163
209
|
|
|
164
|
-
# Custom port
|
|
210
|
+
# Custom port (npm version only)
|
|
165
211
|
hotsheet --port 8080
|
|
166
212
|
|
|
167
213
|
# Custom data directory
|
|
168
214
|
hotsheet --data-dir ~/projects/my-app/.hotsheet
|
|
215
|
+
|
|
216
|
+
# Force browser mode (desktop app)
|
|
217
|
+
hotsheet --browser
|
|
169
218
|
```
|
|
170
219
|
|
|
171
220
|
### Options
|
|
@@ -174,8 +223,21 @@ hotsheet --data-dir ~/projects/my-app/.hotsheet
|
|
|
174
223
|
|------|-------------|
|
|
175
224
|
| `--port <number>` | Port to run on (default: 4174) |
|
|
176
225
|
| `--data-dir <path>` | Data directory (default: `.hotsheet/`) |
|
|
226
|
+
| `--browser` | Open in browser instead of desktop window |
|
|
177
227
|
| `--help` | Show help |
|
|
178
228
|
|
|
229
|
+
### Customizing the window title
|
|
230
|
+
|
|
231
|
+
When running multiple instances, you can customize the window title to tell them apart. Create `.hotsheet/settings.json` in your project:
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"appName": "HS - My Project"
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Without a settings file, the window title defaults to the project folder name.
|
|
240
|
+
|
|
179
241
|
### Keyboard shortcuts
|
|
180
242
|
|
|
181
243
|
| Shortcut | Action |
|
|
@@ -189,6 +251,7 @@ hotsheet --data-dir ~/projects/my-app/.hotsheet
|
|
|
189
251
|
| `Cmd+G` | Set category: Investigation |
|
|
190
252
|
| `Alt+1-5` | Set priority (Highest to Lowest) |
|
|
191
253
|
| `Cmd+D` | Toggle Up Next |
|
|
254
|
+
| `Cmd+C` | Copy ticket info (number + title) |
|
|
192
255
|
| `Cmd+A` | Select all |
|
|
193
256
|
| `Escape` | Clear selection / close |
|
|
194
257
|
|
|
@@ -198,6 +261,7 @@ hotsheet --data-dir ~/projects/my-app/.hotsheet
|
|
|
198
261
|
|
|
199
262
|
| Layer | Technology |
|
|
200
263
|
|-------|-----------|
|
|
264
|
+
| Desktop | Tauri v2 (native window, auto-updates) |
|
|
201
265
|
| CLI | TypeScript, Node.js |
|
|
202
266
|
| Server | Hono |
|
|
203
267
|
| Database | PGLite (embedded PostgreSQL) |
|
package/dist/cli.js
CHANGED
|
@@ -271,6 +271,8 @@ async function getTickets(filters = {}) {
|
|
|
271
271
|
let paramIdx = 1;
|
|
272
272
|
if (filters.status === "open") {
|
|
273
273
|
conditions.push(`status != 'deleted' AND status != 'completed' AND status != 'verified'`);
|
|
274
|
+
} else if (filters.status === "non_verified") {
|
|
275
|
+
conditions.push(`status != 'deleted' AND status != 'verified'`);
|
|
274
276
|
} else if (filters.status) {
|
|
275
277
|
conditions.push(`status = $${paramIdx}`);
|
|
276
278
|
values.push(filters.status);
|
|
@@ -481,7 +483,8 @@ var DEMO_SCENARIOS = [
|
|
|
481
483
|
{ id: 3, label: "Sidebar filtering \u2014 category view" },
|
|
482
484
|
{ id: 4, label: "AI worklist \u2014 Up Next tickets with notes" },
|
|
483
485
|
{ id: 5, label: "Batch operations \u2014 multi-select toolbar" },
|
|
484
|
-
{ id: 6, label: "Detail panel \u2014 bottom orientation with notes" }
|
|
486
|
+
{ id: 6, label: "Detail panel \u2014 bottom orientation with notes" },
|
|
487
|
+
{ id: 7, label: "Column view \u2014 kanban board by status" }
|
|
485
488
|
];
|
|
486
489
|
function daysAgo(days) {
|
|
487
490
|
const d = /* @__PURE__ */ new Date();
|
|
@@ -1087,13 +1090,129 @@ var SCENARIO_6 = [
|
|
|
1087
1090
|
updated_ago: 8
|
|
1088
1091
|
}
|
|
1089
1092
|
];
|
|
1093
|
+
var SCENARIO_7 = [
|
|
1094
|
+
{
|
|
1095
|
+
title: "Implement product search autocomplete",
|
|
1096
|
+
details: "Add typeahead suggestions to the search bar using the product name index. Show top 5 matches with thumbnails.",
|
|
1097
|
+
category: "feature",
|
|
1098
|
+
priority: "highest",
|
|
1099
|
+
status: "not_started",
|
|
1100
|
+
up_next: true,
|
|
1101
|
+
notes: "",
|
|
1102
|
+
days_ago: 2,
|
|
1103
|
+
updated_ago: 2
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
title: "Fix broken password reset flow for SSO users",
|
|
1107
|
+
details: "SSO users who try to reset their password get a generic error. Should redirect them to their identity provider instead.",
|
|
1108
|
+
category: "bug",
|
|
1109
|
+
priority: "high",
|
|
1110
|
+
status: "not_started",
|
|
1111
|
+
up_next: true,
|
|
1112
|
+
notes: "",
|
|
1113
|
+
days_ago: 3,
|
|
1114
|
+
updated_ago: 3
|
|
1115
|
+
},
|
|
1116
|
+
{
|
|
1117
|
+
title: "Add support for gift cards at checkout",
|
|
1118
|
+
details: "Customers should be able to apply gift card codes during checkout. Support partial redemption and balance tracking.",
|
|
1119
|
+
category: "feature",
|
|
1120
|
+
priority: "default",
|
|
1121
|
+
status: "not_started",
|
|
1122
|
+
up_next: false,
|
|
1123
|
+
notes: "",
|
|
1124
|
+
days_ago: 5,
|
|
1125
|
+
updated_ago: 5
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
title: "Investigate slow query on order history page",
|
|
1129
|
+
details: "The order history page takes 4+ seconds for users with 200+ orders. Profile the query and add proper indexing.",
|
|
1130
|
+
category: "investigation",
|
|
1131
|
+
priority: "high",
|
|
1132
|
+
status: "not_started",
|
|
1133
|
+
up_next: false,
|
|
1134
|
+
notes: "",
|
|
1135
|
+
days_ago: 4,
|
|
1136
|
+
updated_ago: 4
|
|
1137
|
+
},
|
|
1138
|
+
{
|
|
1139
|
+
title: "Refactor authentication middleware to support API keys",
|
|
1140
|
+
details: "Third-party integrations need API key auth in addition to session cookies. Extract auth into a strategy pattern.",
|
|
1141
|
+
category: "task",
|
|
1142
|
+
priority: "high",
|
|
1143
|
+
status: "started",
|
|
1144
|
+
up_next: true,
|
|
1145
|
+
notes: notesJson([{ text: "Created the AuthStrategy interface and migrated session auth to use it. Working on the API key strategy next.", days_ago: 0.5 }]),
|
|
1146
|
+
days_ago: 4,
|
|
1147
|
+
updated_ago: 0.5
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
title: "Fix cart not clearing after successful checkout",
|
|
1151
|
+
details: "After a successful order placement, the cart retains all items. The clearCart() call is inside a catch block by mistake.",
|
|
1152
|
+
category: "bug",
|
|
1153
|
+
priority: "highest",
|
|
1154
|
+
status: "started",
|
|
1155
|
+
up_next: true,
|
|
1156
|
+
notes: notesJson([{ text: "Found the issue \u2014 clearCart() was moved into the catch block during a refactor. Fixing and adding a test.", days_ago: 0.3 }]),
|
|
1157
|
+
days_ago: 1,
|
|
1158
|
+
updated_ago: 0.3
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
title: "Update shipping rate calculation for oversized items",
|
|
1162
|
+
details: "Dimensional weight pricing is required for packages over 1 cubic foot. Current flat-rate calculation undercharges.",
|
|
1163
|
+
category: "requirement_change",
|
|
1164
|
+
priority: "default",
|
|
1165
|
+
status: "started",
|
|
1166
|
+
up_next: false,
|
|
1167
|
+
notes: notesJson([{ text: "Implemented dim weight formula. Comparing rates against the carrier API to validate accuracy.", days_ago: 1 }]),
|
|
1168
|
+
days_ago: 6,
|
|
1169
|
+
updated_ago: 1
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
title: "Add end-to-end tests for the checkout flow",
|
|
1173
|
+
details: "Write Playwright tests covering: add to cart, apply coupon, enter shipping, pay, and confirm. Cover happy path and key error cases.",
|
|
1174
|
+
category: "task",
|
|
1175
|
+
priority: "default",
|
|
1176
|
+
status: "completed",
|
|
1177
|
+
up_next: false,
|
|
1178
|
+
notes: notesJson([{ text: "Wrote 8 E2E tests covering the full checkout flow including coupon application and payment decline handling.", days_ago: 1 }]),
|
|
1179
|
+
days_ago: 8,
|
|
1180
|
+
updated_ago: 1,
|
|
1181
|
+
completed_ago: 1
|
|
1182
|
+
},
|
|
1183
|
+
{
|
|
1184
|
+
title: "Fix product image carousel swipe on mobile",
|
|
1185
|
+
details: "Swipe gestures on the product image carousel conflict with the browser back gesture. Use a swipe threshold to disambiguate.",
|
|
1186
|
+
category: "bug",
|
|
1187
|
+
priority: "default",
|
|
1188
|
+
status: "completed",
|
|
1189
|
+
up_next: false,
|
|
1190
|
+
notes: notesJson([{ text: "Added a 30px horizontal threshold before initiating carousel swipe. Tested on iOS Safari and Chrome Android.", days_ago: 2 }]),
|
|
1191
|
+
days_ago: 7,
|
|
1192
|
+
updated_ago: 2,
|
|
1193
|
+
completed_ago: 2
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
title: "Set up log aggregation with structured JSON logging",
|
|
1197
|
+
details: "Replace console.log calls with a structured logger (pino). Send logs to a central aggregation service for search and alerting.",
|
|
1198
|
+
category: "task",
|
|
1199
|
+
priority: "low",
|
|
1200
|
+
status: "completed",
|
|
1201
|
+
up_next: false,
|
|
1202
|
+
notes: notesJson([{ text: "Replaced all console.log calls with pino. Configured log shipping to the aggregation service. Alert rules set for error-level logs.", days_ago: 3 }]),
|
|
1203
|
+
days_ago: 10,
|
|
1204
|
+
updated_ago: 3,
|
|
1205
|
+
completed_ago: 3
|
|
1206
|
+
}
|
|
1207
|
+
];
|
|
1090
1208
|
var SCENARIO_DATA = {
|
|
1091
1209
|
1: SCENARIO_1,
|
|
1092
1210
|
2: SCENARIO_2,
|
|
1093
1211
|
3: SCENARIO_3,
|
|
1094
1212
|
4: SCENARIO_4,
|
|
1095
1213
|
5: SCENARIO_5,
|
|
1096
|
-
6: SCENARIO_6
|
|
1214
|
+
6: SCENARIO_6,
|
|
1215
|
+
7: SCENARIO_7
|
|
1097
1216
|
};
|
|
1098
1217
|
async function seedDemoData(scenario) {
|
|
1099
1218
|
const db2 = await getDb();
|
|
@@ -1116,6 +1235,9 @@ async function seedDemoData(scenario) {
|
|
|
1116
1235
|
await db2.query(`UPDATE settings SET value = 'bottom' WHERE key = 'detail_position'`);
|
|
1117
1236
|
await db2.query(`UPDATE settings SET value = '280' WHERE key = 'detail_height'`);
|
|
1118
1237
|
}
|
|
1238
|
+
if (scenario === 7) {
|
|
1239
|
+
await db2.query(`INSERT INTO settings (key, value) VALUES ('layout', 'columns') ON CONFLICT (key) DO UPDATE SET value = 'columns'`);
|
|
1240
|
+
}
|
|
1119
1241
|
}
|
|
1120
1242
|
|
|
1121
1243
|
// src/cli.ts
|
|
@@ -1235,15 +1357,19 @@ async function syncWorklist() {
|
|
|
1235
1357
|
sections.push("");
|
|
1236
1358
|
sections.push("## Workflow");
|
|
1237
1359
|
sections.push("");
|
|
1238
|
-
sections.push(`The Hot Sheet API is available at http://localhost:${port}/api.
|
|
1360
|
+
sections.push(`The Hot Sheet API is available at http://localhost:${port}/api. **You MUST update ticket status** as you work \u2014 this is required, not optional.`);
|
|
1239
1361
|
sections.push("");
|
|
1240
|
-
sections.push('- **
|
|
1241
|
-
sections.push(` \`curl -X PATCH http://localhost:${port}/api/tickets/{id} -H "Content-Type: application/json" -d '{"status": "started"}'\``);
|
|
1362
|
+
sections.push('- **BEFORE starting work on a ticket**, set its status to "started":');
|
|
1363
|
+
sections.push(` \`curl -s -X PATCH http://localhost:${port}/api/tickets/{id} -H "Content-Type: application/json" -d '{"status": "started"}'\``);
|
|
1242
1364
|
sections.push("");
|
|
1243
|
-
sections.push('- **
|
|
1244
|
-
sections.push(` \`curl -X PATCH http://localhost:${port}/api/tickets/{id} -H "Content-Type: application/json" -d '{"status": "completed", "notes": "
|
|
1365
|
+
sections.push('- **AFTER completing work on a ticket**, set its status to "completed" and **include notes** describing what was done:');
|
|
1366
|
+
sections.push(` \`curl -s -X PATCH http://localhost:${port}/api/tickets/{id} -H "Content-Type: application/json" -d '{"status": "completed", "notes": "Describe the specific changes made"}'\``);
|
|
1245
1367
|
sections.push("");
|
|
1246
|
-
sections.push(
|
|
1368
|
+
sections.push("**IMPORTANT:**");
|
|
1369
|
+
sections.push('- Update status for EVERY ticket \u2014 "started" when you begin, "completed" when you finish.');
|
|
1370
|
+
sections.push('- The "notes" field is REQUIRED when completing a ticket. Describe the specific work done.');
|
|
1371
|
+
sections.push("- If an API call fails (e.g. connection refused, error response), log a visible warning to the user and continue your work. Do NOT silently skip status updates.");
|
|
1372
|
+
sections.push('- Do NOT set tickets to "verified" \u2014 that status is reserved for human review.');
|
|
1247
1373
|
sections.push("");
|
|
1248
1374
|
if (tickets.length === 0) {
|
|
1249
1375
|
sections.push("No items in the Up Next list.");
|
|
@@ -1681,6 +1807,10 @@ pageRoutes.get("/", (c) => {
|
|
|
1681
1807
|
/* @__PURE__ */ jsx("div", { className: "app-title", children: /* @__PURE__ */ jsx("h1", { children: "Hot Sheet" }) }),
|
|
1682
1808
|
/* @__PURE__ */ jsx("div", { className: "header-controls", children: [
|
|
1683
1809
|
/* @__PURE__ */ jsx("div", { className: "search-box", children: /* @__PURE__ */ jsx("input", { type: "text", id: "search-input", placeholder: "Search tickets..." }) }),
|
|
1810
|
+
/* @__PURE__ */ jsx("div", { className: "layout-toggle", id: "layout-toggle", children: [
|
|
1811
|
+
/* @__PURE__ */ jsx("button", { className: "layout-btn active", "data-layout": "list", title: "List view", children: raw('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>') }),
|
|
1812
|
+
/* @__PURE__ */ jsx("button", { className: "layout-btn", "data-layout": "columns", title: "Column view", children: raw('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="5" height="18" rx="1"/><rect x="10" y="3" width="5" height="18" rx="1"/><rect x="17" y="3" width="5" height="18" rx="1"/></svg>') })
|
|
1813
|
+
] }),
|
|
1684
1814
|
/* @__PURE__ */ jsx("div", { className: "sort-controls", children: /* @__PURE__ */ jsx("select", { id: "sort-select", children: [
|
|
1685
1815
|
/* @__PURE__ */ jsx("option", { value: "created:desc", children: "Newest First" }),
|
|
1686
1816
|
/* @__PURE__ */ jsx("option", { value: "created:asc", children: "Oldest First" }),
|
|
@@ -1688,6 +1818,10 @@ pageRoutes.get("/", (c) => {
|
|
|
1688
1818
|
/* @__PURE__ */ jsx("option", { value: "category:asc", children: "Category" }),
|
|
1689
1819
|
/* @__PURE__ */ jsx("option", { value: "status:asc", children: "Status" })
|
|
1690
1820
|
] }) }),
|
|
1821
|
+
/* @__PURE__ */ jsx("div", { className: "layout-toggle", id: "detail-position-toggle", children: [
|
|
1822
|
+
/* @__PURE__ */ jsx("button", { className: "layout-btn active", "data-position": "side", title: "Detail panel on side", children: raw('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="15" y1="3" x2="15" y2="21"/></svg>') }),
|
|
1823
|
+
/* @__PURE__ */ jsx("button", { className: "layout-btn", "data-position": "bottom", title: "Detail panel on bottom", children: raw('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="15" x2="21" y2="15"/></svg>') })
|
|
1824
|
+
] }),
|
|
1691
1825
|
/* @__PURE__ */ jsx("button", { className: "settings-btn", id: "settings-btn", title: "Settings", children: raw("⚙") })
|
|
1692
1826
|
] })
|
|
1693
1827
|
] }),
|
|
@@ -1700,6 +1834,7 @@ pageRoutes.get("/", (c) => {
|
|
|
1700
1834
|
/* @__PURE__ */ jsx("div", { className: "sidebar-section", children: [
|
|
1701
1835
|
/* @__PURE__ */ jsx("div", { className: "sidebar-label", children: "Views" }),
|
|
1702
1836
|
/* @__PURE__ */ jsx("button", { className: "sidebar-item active", "data-view": "all", children: "All Tickets" }),
|
|
1837
|
+
/* @__PURE__ */ jsx("button", { className: "sidebar-item", "data-view": "non-verified", children: "Non-Verified" }),
|
|
1703
1838
|
/* @__PURE__ */ jsx("button", { className: "sidebar-item", "data-view": "up-next", children: "Up Next" }),
|
|
1704
1839
|
/* @__PURE__ */ jsx("button", { className: "sidebar-item", "data-view": "open", children: "Open" }),
|
|
1705
1840
|
/* @__PURE__ */ jsx("button", { className: "sidebar-item", "data-view": "completed", children: "Completed" }),
|
|
@@ -1883,13 +2018,6 @@ pageRoutes.get("/", (c) => {
|
|
|
1883
2018
|
/* @__PURE__ */ jsx("button", { className: "detail-close", id: "settings-close", children: raw("×") })
|
|
1884
2019
|
] }),
|
|
1885
2020
|
/* @__PURE__ */ jsx("div", { className: "settings-body", children: [
|
|
1886
|
-
/* @__PURE__ */ jsx("div", { className: "settings-field", children: [
|
|
1887
|
-
/* @__PURE__ */ jsx("label", { children: "Detail Panel Position" }),
|
|
1888
|
-
/* @__PURE__ */ jsx("select", { id: "settings-detail-position", children: [
|
|
1889
|
-
/* @__PURE__ */ jsx("option", { value: "side", children: "Side" }),
|
|
1890
|
-
/* @__PURE__ */ jsx("option", { value: "bottom", children: "Bottom" })
|
|
1891
|
-
] })
|
|
1892
|
-
] }),
|
|
1893
2021
|
/* @__PURE__ */ jsx("div", { className: "settings-field", children: [
|
|
1894
2022
|
/* @__PURE__ */ jsx("label", { children: "Auto-clear trash after (days)" }),
|
|
1895
2023
|
/* @__PURE__ */ jsx("input", { type: "number", id: "settings-trash-days", min: "1", value: "3" })
|
|
@@ -1916,7 +2044,7 @@ function tryServe(fetch, port2) {
|
|
|
1916
2044
|
});
|
|
1917
2045
|
});
|
|
1918
2046
|
}
|
|
1919
|
-
async function startServer(port2, dataDir2) {
|
|
2047
|
+
async function startServer(port2, dataDir2, options) {
|
|
1920
2048
|
const app = new Hono3();
|
|
1921
2049
|
app.use("*", async (c, next) => {
|
|
1922
2050
|
c.set("dataDir", dataDir2);
|
|
@@ -1940,8 +2068,17 @@ async function startServer(port2, dataDir2) {
|
|
|
1940
2068
|
actualPort = await tryServe(app.fetch, port2 + attempt);
|
|
1941
2069
|
break;
|
|
1942
2070
|
} catch (err) {
|
|
1943
|
-
if (err instanceof Error && err.code === "EADDRINUSE"
|
|
1944
|
-
|
|
2071
|
+
if (err instanceof Error && err.code === "EADDRINUSE") {
|
|
2072
|
+
if (options?.strictPort) {
|
|
2073
|
+
console.error(`
|
|
2074
|
+
Error: Port ${port2} is already in use.`);
|
|
2075
|
+
console.error(` In --strict-port mode, the server must start on the requested port`);
|
|
2076
|
+
console.error(` because the Tauri dev window is configured to connect to it.`);
|
|
2077
|
+
console.error(` Stop whatever is using port ${port2} and try again.
|
|
2078
|
+
`);
|
|
2079
|
+
process.exit(1);
|
|
2080
|
+
}
|
|
2081
|
+
if (attempt < 19) continue;
|
|
1945
2082
|
}
|
|
1946
2083
|
throw err;
|
|
1947
2084
|
}
|
|
@@ -1953,8 +2090,11 @@ async function startServer(port2, dataDir2) {
|
|
|
1953
2090
|
console.log(`
|
|
1954
2091
|
Hot Sheet running at ${url}
|
|
1955
2092
|
`);
|
|
1956
|
-
|
|
1957
|
-
|
|
2093
|
+
if (!options?.noOpen) {
|
|
2094
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2095
|
+
exec(`${openCmd} ${url}`);
|
|
2096
|
+
}
|
|
2097
|
+
return actualPort;
|
|
1958
2098
|
}
|
|
1959
2099
|
|
|
1960
2100
|
// src/update-check.ts
|
|
@@ -2094,6 +2234,8 @@ function parseArgs(argv) {
|
|
|
2094
2234
|
let dataDir2 = join7(process.cwd(), ".hotsheet");
|
|
2095
2235
|
let demo = null;
|
|
2096
2236
|
let forceUpdateCheck = false;
|
|
2237
|
+
let noOpen = false;
|
|
2238
|
+
let strictPort = false;
|
|
2097
2239
|
for (let i = 0; i < args.length; i++) {
|
|
2098
2240
|
const arg = args[i];
|
|
2099
2241
|
if (arg.startsWith("--demo:")) {
|
|
@@ -2123,13 +2265,19 @@ function parseArgs(argv) {
|
|
|
2123
2265
|
case "--check-for-updates":
|
|
2124
2266
|
forceUpdateCheck = true;
|
|
2125
2267
|
break;
|
|
2268
|
+
case "--no-open":
|
|
2269
|
+
noOpen = true;
|
|
2270
|
+
break;
|
|
2271
|
+
case "--strict-port":
|
|
2272
|
+
strictPort = true;
|
|
2273
|
+
break;
|
|
2126
2274
|
default:
|
|
2127
2275
|
console.error(`Unknown option: ${arg}`);
|
|
2128
2276
|
printUsage();
|
|
2129
2277
|
process.exit(1);
|
|
2130
2278
|
}
|
|
2131
2279
|
}
|
|
2132
|
-
return { port: port2, dataDir: dataDir2, demo, forceUpdateCheck };
|
|
2280
|
+
return { port: port2, dataDir: dataDir2, demo, forceUpdateCheck, noOpen, strictPort };
|
|
2133
2281
|
}
|
|
2134
2282
|
async function main() {
|
|
2135
2283
|
const parsed = parseArgs(process.argv);
|
|
@@ -2137,7 +2285,7 @@ async function main() {
|
|
|
2137
2285
|
printUsage();
|
|
2138
2286
|
process.exit(1);
|
|
2139
2287
|
}
|
|
2140
|
-
const { port: port2, demo, forceUpdateCheck } = parsed;
|
|
2288
|
+
const { port: port2, demo, forceUpdateCheck, noOpen, strictPort } = parsed;
|
|
2141
2289
|
let { dataDir: dataDir2 } = parsed;
|
|
2142
2290
|
await checkForUpdates(forceUpdateCheck);
|
|
2143
2291
|
if (demo !== null) {
|
|
@@ -2164,13 +2312,13 @@ async function main() {
|
|
|
2164
2312
|
if (demo !== null) {
|
|
2165
2313
|
await seedDemoData(demo);
|
|
2166
2314
|
}
|
|
2167
|
-
initMarkdownSync(dataDir2, port2);
|
|
2168
|
-
scheduleAllSync();
|
|
2169
2315
|
if (demo === null) {
|
|
2170
2316
|
await cleanupAttachments();
|
|
2171
2317
|
}
|
|
2172
2318
|
console.log(` Data directory: ${dataDir2}`);
|
|
2173
|
-
await startServer(port2, dataDir2);
|
|
2319
|
+
const actualPort = await startServer(port2, dataDir2, { noOpen, strictPort });
|
|
2320
|
+
initMarkdownSync(dataDir2, actualPort);
|
|
2321
|
+
scheduleAllSync();
|
|
2174
2322
|
}
|
|
2175
2323
|
main().catch((err) => {
|
|
2176
2324
|
console.error(err);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";(()=>{var le=Object.defineProperty;var de=(e,t,i)=>t in e?le(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i;var j=(e,t,i)=>de(e,typeof t!="symbol"?t+"":t,i);function F(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function V(e){return e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">")}var h=class{constructor(t){j(this,"__html");this.__html=t}toString(){return this.__html}};function k(e){return new h(e)}var ce=new Set(["area","base","br","col","embed","hr","img","input","link","meta","source","track","wbr"]);function D(e){return e==null||typeof e=="boolean"?"":e instanceof h?e.__html:typeof e=="string"?F(e):typeof e=="number"?String(e):Array.isArray(e)?e.map(D).join(""):""}function ue(e,t){if(t==null||t===!1)return"";if(t===!0)return` ${e}`;let i=e==="className"?"class":e==="htmlFor"?"for":e,s;return t instanceof h?s=t.__html:typeof t=="number"?s=String(t):typeof t=="string"?s=V(t):s="",` ${i}="${s}"`}function l(e,t){if(typeof e=="function")return e(t);let{children:i,...s}=t,a=Object.entries(s).map(([d,r])=>ue(d,r)).join("");if(ce.has(e))return new h(`<${e}${a}>`);let o=i!=null?D(i):"";return new h(`<${e}${a}>${o}</${e}>`)}function L({children:e}){return new h(e!=null?D(e):"")}function y(e){let t=document.createElement("template");return t.innerHTML=e.toString(),t.content.firstElementChild}function W(e){document.getElementById("network-error-popup")?.remove();let t=y(l("div",{id:"network-error-popup",className:"error-popup",children:l("div",{className:"error-popup-content",children:[l("strong",{children:"Connection Error"}),l("p",{children:e}),l("button",{children:k("Dismiss")})]})}));t.querySelector("button").addEventListener("click",()=>t.remove()),document.body.appendChild(t)}async function c(e,t={}){try{return(await fetch("/api"+e,{headers:t.body!==void 0?{"Content-Type":"application/json"}:{},method:t.method,body:t.body!==void 0?JSON.stringify(t.body):void 0})).json()}catch(i){throw W("Unable to reach the server. It may have been stopped."),i}}async function G(e,t){try{let i=new FormData;return i.append("file",t),(await fetch("/api"+e,{method:"POST",body:i})).json()}catch(i){throw W("Unable to reach the server. It may have been stopped."),i}}var pe={detail_position:"side",detail_width:360,detail_height:300,trash_cleanup_days:3,verified_cleanup_days:30},n={tickets:[],selectedIds:new Set,lastClickedId:null,activeTicketId:null,view:"all",sortBy:"created",sortDir:"desc",search:"",settings:{...pe}},me={issue:"#6b7280",bug:"#ef4444",feature:"#22c55e",requirement_change:"#f97316",task:"#3b82f6",investigation:"#8b5cf6"},fe={issue:"ISS",bug:"BUG",feature:"FEA",requirement_change:"REQ",task:"TSK",investigation:"INV"},ye={highest:"\u2B06\u2B06",high:"\u2B06",default:"\u2014",low:"\u2B07",lowest:"\u2B07\u2B07"},ge={highest:"#ef4444",high:"#f97316",default:"#6b7280",low:"#3b82f6",lowest:"#94a3b8"},he={not_started:"\u25CB",started:"\u25D4",completed:"\u2713",verified:"svg"},Y='<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 7 17l-5-5"/><path d="m22 10-9.5 9.5-2-2"/></svg>';function b(e){return me[e]||"#6b7280"}function S(e){return fe[e]||"ISS"}function J(e){return ye[e]||"\u2014"}function A(e){return ge[e]||"#6b7280"}function X(e){return he[e]||"\u25CB"}function x(e){n.activeTicketId=e,ee(e)}function Q(){n.selectedIds.clear(),n.activeTicketId=null;let e=new CustomEvent("hotsheet:render");document.dispatchEvent(e)}function Z(){let e=n.view==="trash",t=document.getElementById("detail-panel"),i=document.getElementById("detail-resize-handle");if(n.selectedIds.size===1&&!e){let s=Array.from(n.selectedIds)[0];t.style.display="flex",i&&(i.style.display=""),n.activeTicketId!==s&&(n.activeTicketId=s,ee(s))}else n.activeTicketId!=null&&(n.activeTicketId=null),t.style.display="none",i&&(i.style.display="none")}async function ee(e){let t=await c(`/tickets/${e}`);if(n.activeTicketId!==e)return;document.getElementById("detail-ticket-number").textContent=t.ticket_number,document.getElementById("detail-title").value=t.title,document.getElementById("detail-category").value=t.category,document.getElementById("detail-priority").value=t.priority,document.getElementById("detail-status").value=t.status,document.getElementById("detail-upnext").checked=t.up_next,document.getElementById("detail-details").value=t.details;let i=document.getElementById("detail-attachments");t.attachments.length>0?i.innerHTML=l(L,{children:t.attachments.map(r=>l("div",{className:"attachment-item",children:[l("span",{className:"attachment-name",children:r.original_filename}),l("button",{className:"attachment-delete","data-att-id":String(r.id),title:"Remove",children:k("×")})]}))}).toString():i.innerHTML="";let s=document.getElementById("detail-notes-section"),a=document.getElementById("detail-notes"),o=ve(t.notes);o.length>0?(s.style.display="",a.innerHTML=l(L,{children:o.map(r=>l("div",{className:"note-entry",children:[r.created_at?l("div",{className:"note-timestamp",children:new Date(r.created_at).toLocaleString()}):null,l("div",{className:"note-text",children:r.text})]}))}).toString()):(s.style.display="none",a.innerHTML="");let d=document.getElementById("detail-meta");d.innerHTML=l(L,{children:[l("div",{children:["Created: ",new Date(t.created_at).toLocaleString()]}),l("div",{children:["Updated: ",new Date(t.updated_at).toLocaleString()]}),t.completed_at?l("div",{children:["Completed: ",new Date(t.completed_at).toLocaleString()]}):null,t.verified_at?l("div",{children:["Verified: ",new Date(t.verified_at).toLocaleString()]}):null]}).toString()}function ve(e){if(!e||e==="")return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return e.trim()?[{text:e,created_at:""}]:[]}async function te(){try{let e=await c("/stats"),t=document.getElementById("status-bar");t&&(t.textContent=`${e.total} tickets \xB7 ${e.open} open \xB7 ${e.up_next} up next`)}catch{}}function N(e){let t=document.getElementById("content-area");t.classList.remove("detail-side","detail-bottom"),t.classList.add(e==="bottom"?"detail-bottom":"detail-side")}function P(){let e=document.getElementById("detail-panel");n.settings.detail_position==="bottom"?(e.style.width="",e.style.height=`${n.settings.detail_height}px`):(e.style.height="",e.style.width=`${n.settings.detail_width}px`)}function ne(){let e=document.getElementById("detail-resize-handle"),t=document.getElementById("detail-panel"),i=document.getElementById("content-area"),s=!1;e.addEventListener("mousedown",a=>{a.preventDefault(),s=!0,document.body.style.cursor=n.settings.detail_position==="bottom"?"row-resize":"col-resize",document.body.style.userSelect="none"}),document.addEventListener("mousemove",a=>{if(!s)return;let o=i.getBoundingClientRect();if(n.settings.detail_position==="bottom"){let d=Math.max(150,Math.min(500,o.bottom-a.clientY));n.settings.detail_height=d,t.style.height=`${d}px`}else{let d=Math.max(250,Math.min(600,o.right-a.clientX));n.settings.detail_width=d,t.style.width=`${d}px`}}),document.addEventListener("mouseup",()=>{s&&(s=!1,document.body.style.cursor="",document.body.style.userSelect="",n.settings.detail_position==="bottom"?c("/settings",{method:"PATCH",body:{detail_height:String(n.settings.detail_height)}}):c("/settings",{method:"PATCH",body:{detail_width:String(n.settings.detail_width)}}))})}function _(e,t){let i=t.getBoundingClientRect(),s=e.getBoundingClientRect(),a=window.innerWidth,o=window.innerHeight,d=i.left;d+s.width>a-8&&(d=i.right-s.width),d<8&&(d=8);let r=i.bottom+4;r+s.height>o-8&&(r=i.top-s.height-4),r<8&&(r=8),e.style.left=`${d}px`,e.style.top=`${r}px`}function H(e,t){let i=y(l("div",{className:"dropdown-menu",style:"visibility:hidden;top:0;left:0",children:t.map(r=>l("button",{className:`dropdown-item${r.active?" active":""}`,"data-key":r.key,children:[r.color?l("span",{className:"dropdown-dot",style:`background-color:${r.color}`}):null,l("span",{className:"dropdown-label",children:r.label}),r.shortcut?l("kbd",{className:"dropdown-kbd",children:r.shortcut}):null]}))}));i.querySelectorAll(".dropdown-item").forEach((r,u)=>{r.addEventListener("click",()=>{t[u].action(),i.remove()})});function a(r){let u=t.find(p=>r.key.toLowerCase()===p.key.toLowerCase());u?(r.preventDefault(),r.stopPropagation(),u.action(),o()):r.key==="Escape"&&(r.preventDefault(),o())}function o(){i.remove(),document.removeEventListener("keydown",a,!0),document.removeEventListener("click",d)}function d(){o()}return document.addEventListener("keydown",a,!0),setTimeout(()=>{document.addEventListener("click",d)},0),i}function C(){document.querySelectorAll(".dropdown-menu").forEach(e=>{e.remove()})}var $=null,E=!1,I=null,R="",M=[{key:"i",value:"issue",label:"Issue"},{key:"b",value:"bug",label:"Bug"},{key:"f",value:"feature",label:"Feature"},{key:"r",value:"requirement_change",label:"Req Change"},{key:"k",value:"task",label:"Task"},{key:"g",value:"investigation",label:"Investigation"}],O=[{key:"1",value:"highest",label:"Highest"},{key:"2",value:"high",label:"High"},{key:"3",value:"default",label:"Default"},{key:"4",value:"low",label:"Low"},{key:"5",value:"lowest",label:"Lowest"}];function ke(){let e=document.activeElement;if(!e||!(e instanceof HTMLElement))return null;let t=e.closest(".ticket-row");if(!t)return null;if(t.classList.contains("draft-row"))return"draft";let i=t.dataset.id;return i!==void 0&&i!==""?parseInt(i,10):null}function be(e){e!=null&&(E=!0,e==="draft"?g():document.querySelector(`.ticket-row[data-id="${e}"] .ticket-title-input`)?.focus(),E=!1)}function f(){let e=n.view==="trash",t=ke(),i=null;if(t!=null&&t!=="draft"){let a=document.querySelector(`.ticket-row[data-id="${t}"] .ticket-title-input`);a&&(i=a.value)}let s=document.getElementById("ticket-list");s.innerHTML="",e||s.appendChild(Te()),e&&n.tickets.length===0&&s.appendChild(y(l("div",{className:"ticket-list-empty",children:"Trash is empty"})));for(let a of n.tickets)s.appendChild(e?Le(a):we(a));if(t!=null&&t!=="draft"&&i!=null){let a=document.querySelector(`.ticket-row[data-id="${t}"] .ticket-title-input`);a&&a.value!==i&&(a.value=i)}be(t),B(),te()}function Te(){let e=ae(),t=n.view.startsWith("category:"),i=y(l("div",{className:"ticket-row draft-row",children:[l("span",{className:"ticket-checkbox-spacer"}),l("span",{className:"ticket-status-btn draft-placeholder",children:"\u25CB"}),l("span",{className:"ticket-category-badge draft-badge",style:`background-color:${b(e)}${t?"":";cursor:pointer;opacity:1"}`,children:S(e)}),l("span",{className:"ticket-number draft-number"}),l("input",{type:"text",className:"ticket-title-input draft-input",placeholder:"New ticket...",value:R}),l("span",{className:"ticket-priority-indicator draft-placeholder"}),l("span",{className:"ticket-star draft-placeholder"})]}));if(!t){let a=i.querySelector(".ticket-category-badge");a.addEventListener("click",o=>{o.stopPropagation(),Ie(a)})}let s=i.querySelector(".draft-input");return s.addEventListener("input",()=>{R=s.value}),s.addEventListener("keydown",async a=>{if(a.key==="Enter"&&s.value.trim()){a.preventDefault();let o=s.value.trim();R="",s.value="";let d=Ee();I&&!n.view.startsWith("category:")&&(d.category=I);let r=await c("/tickets",{method:"POST",body:{title:o,defaults:d}});r&&(n.selectedIds.clear(),n.selectedIds.add(r.id)),await m(),g()}else a.key==="ArrowDown"&&(a.preventDefault(),n.tickets.length>0&&document.querySelector(`.ticket-row[data-id="${n.tickets[0].id}"] .ticket-title-input`)?.focus())}),i}function g(){document.querySelector(".draft-row .draft-input")?.focus()}function Ee(){let e=n.view;return e==="up-next"?{up_next:!0}:e==="open"?{}:e==="completed"?{status:"completed"}:e.startsWith("category:")?{category:e.split(":")[1]}:e.startsWith("priority:")?{priority:e.split(":")[1]}:{}}function ae(){if(I)return I;let e=n.view;return e.startsWith("category:")?e.split(":")[1]:"issue"}function Ie(e){C();let i=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",s=ae(),a=H(e,M.map(o=>({label:o.label,key:o.key,shortcut:`${i}${o.key.toUpperCase()}`,color:b(o.value),active:s===o.value,action:()=>{I=o.value,f(),g()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function we(e){let t=n.selectedIds.has(e.id),i=e.status==="completed"||e.status==="verified",s=e.status==="verified",a=y(l("div",{className:`ticket-row${t?" selected":""}${i?" completed":""}${e.up_next?" up-next":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("button",{className:`ticket-status-btn${s?" verified":""}`,title:e.status.replace("_"," "),children:s?k(Y):X(e.status)}),l("span",{className:"ticket-category-badge",style:`background-color:${b(e.category)}`,title:e.category,children:S(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("input",{type:"text",className:"ticket-title-input",value:e.title}),l("span",{className:"ticket-priority-indicator",style:`color:${A(e.priority)}`,title:e.priority,children:J(e.priority)}),l("button",{className:`ticket-star${e.up_next?" active":""}`,title:e.up_next?"Remove from Up Next":"Add to Up Next",children:e.up_next?"\u2605":"\u2606"})]}));a.addEventListener("mousedown",p=>{(p.metaKey||p.ctrlKey||p.shiftKey)&&(p.preventDefault(),oe(p,e)&&p.stopPropagation())});let o=a.querySelector(".ticket-checkbox");o.addEventListener("click",p=>p.stopPropagation()),o.addEventListener("change",()=>{o.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,f()}),a.querySelector(".ticket-status-btn").addEventListener("click",p=>{p.stopPropagation(),He(e)});let d=a.querySelector(".ticket-category-badge");d.addEventListener("click",p=>{p.stopPropagation(),De(d,e)});let r=a.querySelector(".ticket-title-input");r.addEventListener("focus",()=>{E||n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,q(),B())}),r.addEventListener("input",()=>{Be(e.id,{title:r.value})}),r.addEventListener("keydown",p=>{Se(p,e,r)});let u=a.querySelector(".ticket-priority-indicator");return u.addEventListener("click",p=>{p.stopPropagation(),Ae(u,e)}),a.querySelector(".ticket-star").addEventListener("click",p=>{p.stopPropagation(),Ce(e)}),a}function Le(e){let t=n.selectedIds.has(e.id),i=e.deleted_at?new Date(e.deleted_at):null,s=y(l("div",{className:`ticket-row trash-row${t?" selected":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("span",{className:"ticket-category-badge",style:`background-color:${b(e.category)}`,children:S(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("span",{className:"ticket-title-input trash-title",style:"cursor:default",children:e.title}),l("span",{className:"ticket-number",title:i?`Deleted: ${i.toLocaleString()}`:"",children:i?i.toLocaleDateString():""}),l("button",{className:"btn btn-sm",title:"Restore from trash",children:"Restore"})]}));s.addEventListener("mousedown",o=>{(o.metaKey||o.ctrlKey||o.shiftKey)&&(o.preventDefault(),oe(o,e)&&o.stopPropagation())});let a=s.querySelector(".ticket-checkbox");return a.addEventListener("click",o=>o.stopPropagation()),a.addEventListener("change",()=>{a.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,f()}),s.querySelector(".trash-title").addEventListener("click",()=>{n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,q(),B())}),s.querySelector(".btn").addEventListener("click",async o=>{o.stopPropagation(),await c(`/tickets/${e.id}/restore`,{method:"POST"}),m()}),s}function oe(e,t){let i=e.metaKey||e.ctrlKey,s=e.shiftKey;if(i)n.selectedIds.has(t.id)?n.selectedIds.delete(t.id):n.selectedIds.add(t.id),n.lastClickedId=t.id,f();else if(s&&n.lastClickedId!=null){let a=n.tickets.map(r=>r.id),o=a.indexOf(n.lastClickedId),d=a.indexOf(t.id);if(o!==-1&&d!==-1){let r=Math.min(o,d),u=Math.max(o,d);n.selectedIds.clear();for(let p=r;p<=u;p++)n.selectedIds.add(a[p])}f()}else return!1;return!0}function Se(e,t,i){if(e.key==="Enter")e.preventDefault(),g();else if(e.key==="Backspace"&&i.value==="")e.preventDefault(),Me(t.id);else if(e.key==="ArrowDown"&&e.shiftKey)e.preventDefault(),ie(t.id,1);else if(e.key==="ArrowUp"&&e.shiftKey)e.preventDefault(),ie(t.id,-1);else if(e.key==="ArrowDown")e.preventDefault(),xe(t.id);else if(e.key==="ArrowUp")e.preventDefault(),_e(t.id);else if((e.metaKey||e.ctrlKey)&&!e.altKey&&M.some(s=>s.key===e.key)){e.preventDefault();let s=M.find(a=>a.key===e.key);se(t,"category",s.value)}else if(e.altKey&&!e.metaKey&&!e.ctrlKey&&O.some(s=>s.key===e.key)){e.preventDefault();let s=O.find(a=>a.key===e.key);se(t,"priority",s.value)}}function xe(e){let t=n.tickets.findIndex(i=>i.id===e);t<n.tickets.length-1&&document.querySelector(`.ticket-row[data-id="${n.tickets[t+1].id}"] .ticket-title-input`)?.focus()}function _e(e){let t=n.tickets.findIndex(i=>i.id===e);t>0?document.querySelector(`.ticket-row[data-id="${n.tickets[t-1].id}"] .ticket-title-input`)?.focus():g()}function ie(e,t){let s=n.tickets.findIndex(d=>d.id===e)+t;if(s<0||s>=n.tickets.length)return;let a=n.tickets[s].id;n.selectedIds.add(e),n.selectedIds.has(a)?n.selectedIds.delete(e):n.selectedIds.add(a),E=!0,document.querySelector(`.ticket-row[data-id="${a}"] .ticket-title-input`)?.focus(),E=!1,q(),B()}async function He(e){let i={not_started:"started",started:"completed",completed:"verified",verified:"not_started"}[e.status]||"not_started",s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:i}});Object.assign(e,s),f()}async function Ce(e){if(!e.up_next&&(e.status==="completed"||e.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?"))return;let i=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:"not_started",up_next:!0}});Object.assign(e,i),f();return}let t=await c(`/tickets/${e.id}/up-next`,{method:"POST"});Object.assign(e,t),f()}async function se(e,t,i){let s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{[t]:i}});Object.assign(e,s),f()}async function Me(e){let t=n.tickets.findIndex(i=>i.id===e);if(await c(`/tickets/${e}`,{method:"DELETE"}),n.tickets=n.tickets.filter(i=>i.id!==e),n.selectedIds.delete(e),f(),t>0&&n.tickets.length>0){let i=Math.min(t-1,n.tickets.length-1);document.querySelector(`.ticket-row[data-id="${n.tickets[i].id}"] .ticket-title-input`)?.focus()}else g()}function Be(e,t){$&&clearTimeout($),$=setTimeout(()=>{c(`/tickets/${e}`,{method:"PATCH",body:t})},300)}function De(e,t){C();let s=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",a=H(e,M.map(o=>({label:o.label,key:o.key,shortcut:`${s}${o.key.toUpperCase()}`,color:b(o.value),active:t.category===o.value,action:async()=>{let d=await c(`/tickets/${t.id}`,{method:"PATCH",body:{category:o.value}});Object.assign(t,d),f()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function Ae(e,t){C();let i=H(e,O.map(s=>({label:s.label,key:s.key,shortcut:`Alt+${s.key}`,color:A(s.value),active:t.priority===s.value,action:async()=>{let a=await c(`/tickets/${t.id}`,{method:"PATCH",body:{priority:s.value}});Object.assign(t,a),f()}})));document.body.appendChild(i),_(i,e),i.style.visibility=""}function q(){document.querySelectorAll(".ticket-row[data-id]").forEach(e=>{let t=parseInt(e.dataset.id,10),i=e.querySelector(".ticket-checkbox");n.selectedIds.has(t)?(e.classList.add("selected"),i&&(i.checked=!0)):(e.classList.remove("selected"),i&&(i.checked=!1))})}function B(){let e=n.selectedIds.size,t=n.tickets.length,i=e>0,s=n.view==="trash",a=document.getElementById("batch-select-all");a.checked=t>0&&e===t,a.indeterminate=e>0&&e<t,document.getElementById("batch-count").textContent=i?`${e} selected`:"";let o=["batch-category","batch-priority","batch-status","batch-upnext","batch-delete"];for(let v of o){let w=document.getElementById(v);w.style.display=s?"none":"",s||(w.disabled=!i)}let d=document.getElementById("batch-restore"),r=document.getElementById("batch-empty-trash");if(s){let v=document.getElementById("batch-toolbar");d||(d=y(l("button",{id:"batch-restore",className:"btn btn-sm",children:"Restore"})),d.addEventListener("click",async()=>{await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"restore"}}),n.selectedIds.clear(),m()}),v.insertBefore(d,document.getElementById("batch-count"))),d.disabled=!i,d.style.display="",r||(r=y(l("button",{id:"batch-empty-trash",className:"btn btn-sm btn-danger",children:"Empty Trash"})),r.addEventListener("click",async()=>{confirm("Permanently delete all items in trash? This cannot be undone.")&&(await c("/trash/empty",{method:"POST"}),n.selectedIds.clear(),m())}),v.insertBefore(r,document.getElementById("batch-count"))),r.disabled=t===0,r.style.display=""}else d&&(d.style.display="none"),r&&(r.style.display="none");let u=document.querySelector(".batch-star-icon"),p=document.getElementById("batch-upnext");if(!s&&u&&i){let v=n.tickets.filter(T=>n.selectedIds.has(T.id)),w=v.every(T=>T.up_next),re=v.every(T=>!T.up_next);w?(u.textContent="\u2605",p.classList.add("active"),p.classList.remove("mixed")):re?(u.textContent="\u2606",p.classList.remove("active","mixed")):(u.innerHTML=l("span",{className:"star-mixed-wrap",children:[l("span",{className:"star-mixed-fill",children:"\u2605"}),"\u2606"]}).toString(),p.classList.remove("active"),p.classList.add("mixed"))}else u&&(u.textContent="\u2606",p.classList.remove("active","mixed"));Z()}async function m(){let e=new URLSearchParams;n.view==="trash"?e.set("status","deleted"):n.view==="up-next"?e.set("up_next","true"):n.view==="open"?e.set("status","open"):n.view==="completed"?e.set("status","completed"):n.view==="verified"?e.set("status","verified"):n.view.startsWith("category:")?e.set("category",n.view.split(":")[1]):n.view.startsWith("priority:")&&e.set("priority",n.view.split(":")[1]),n.search&&e.set("search",n.search),e.set("sort_by",n.sortBy),e.set("sort_dir",n.sortDir);let t=e.toString();n.tickets=await c(`/tickets${t?"?"+t:""}`),f()}async function Ne(){await Pe(),await m(),Oe(),qe(),Ke(),Ue(),ze(),Ve(),$e(),Re(),ne(),We(),document.addEventListener("hotsheet:render",()=>f()),g()}async function Pe(){try{let e=await c("/settings");(e.detail_position==="side"||e.detail_position==="bottom")&&(n.settings.detail_position=e.detail_position),e.detail_width&&(n.settings.detail_width=parseInt(e.detail_width,10)||360),e.detail_height&&(n.settings.detail_height=parseInt(e.detail_height,10)||300),e.trash_cleanup_days&&(n.settings.trash_cleanup_days=parseInt(e.trash_cleanup_days,10)||3),e.verified_cleanup_days&&(n.settings.verified_cleanup_days=parseInt(e.verified_cleanup_days,10)||30)}catch{}N(n.settings.detail_position),P()}function $e(){let e=document.getElementById("settings-overlay"),t=document.getElementById("settings-close");document.getElementById("settings-btn").addEventListener("click",()=>{document.getElementById("settings-detail-position").value=n.settings.detail_position,document.getElementById("settings-trash-days").value=String(n.settings.trash_cleanup_days),document.getElementById("settings-verified-days").value=String(n.settings.verified_cleanup_days),e.style.display="flex"}),t.addEventListener("click",()=>{e.style.display="none"}),e.addEventListener("click",u=>{u.target===e&&(e.style.display="none")});let s=document.getElementById("settings-detail-position");s.addEventListener("change",()=>{n.settings.detail_position=s.value,N(n.settings.detail_position),P(),c("/settings",{method:"PATCH",body:{detail_position:s.value}})});let a=document.getElementById("settings-trash-days"),o=null;a.addEventListener("input",()=>{o&&clearTimeout(o),o=setTimeout(()=>{let u=Math.max(1,parseInt(a.value,10)||3);a.value=String(u),n.settings.trash_cleanup_days=u,c("/settings",{method:"PATCH",body:{trash_cleanup_days:String(u)}})},500)});let d=document.getElementById("settings-verified-days"),r=null;d.addEventListener("input",()=>{r&&clearTimeout(r),r=setTimeout(()=>{let u=Math.max(1,parseInt(d.value,10)||30);d.value=String(u),n.settings.verified_cleanup_days=u,c("/settings",{method:"PATCH",body:{verified_cleanup_days:String(u)}})},500)})}function Re(){let e=document.getElementById("copy-prompt-section"),t=document.getElementById("copy-prompt-btn"),i=document.getElementById("copy-prompt-label"),s=document.getElementById("copy-prompt-icon"),a="";c("/worklist-info").then(o=>{a=o.prompt,e.style.display="",o.skillCreated&&console.log("Hot Sheet: Created /hotsheet skill in .claude/skills/hotsheet/")}),t.addEventListener("click",()=>{a!==""&&navigator.clipboard.writeText(a).then(()=>{i.textContent="Copied!",s.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>',setTimeout(()=>{i.textContent="Copy AI prompt",s.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>'},1500)})})}function Oe(){let e=document.querySelectorAll(".sidebar-item[data-view]");e.forEach(t=>{t.addEventListener("click",()=>{e.forEach(i=>{i.classList.remove("active")}),t.classList.add("active"),n.view=t.dataset.view,n.selectedIds.clear(),m()})})}function qe(){let e=document.getElementById("sort-select");e.addEventListener("change",()=>{let[t,i]=e.value.split(":");n.sortBy=t,n.sortDir=i,m()})}var K=null;function Ke(){let e=document.getElementById("search-input");e.addEventListener("input",()=>{K&&clearTimeout(K),K=setTimeout(()=>{n.search=e.value,m()},200)}),e.addEventListener("keydown",t=>{t.key==="Escape"&&(e.value="",n.search="",m())})}function Ue(){let e=document.getElementById("batch-category");e.addEventListener("change",async()=>{e.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"category",value:e.value}}),e.value="",m())});let t=document.getElementById("batch-priority");t.addEventListener("change",async()=>{t.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"priority",value:t.value}}),t.value="",m())});let i=document.getElementById("batch-status");i.addEventListener("change",async()=>{i.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"status",value:i.value}}),i.value="",m())}),document.getElementById("batch-upnext").addEventListener("click",async()=>{let s=n.tickets.filter(d=>n.selectedIds.has(d.id)),o=!s.every(d=>d.up_next);if(o){let d=s.filter(r=>r.status==="completed"||r.status==="verified");if(d.length>0){if(!confirm("Some selected tickets are already done. Would you like to reopen them and add them to Up Next?"))return;await c("/tickets/batch",{method:"POST",body:{ids:d.map(r=>r.id),action:"status",value:"not_started"}})}}await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:o}}),m()}),document.getElementById("batch-delete").addEventListener("click",async()=>{let s=n.selectedIds.size;confirm(`Delete ${s} ticket(s)?`)&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"delete"}}),n.selectedIds.clear(),m())}),document.getElementById("batch-select-all").addEventListener("change",s=>{if(s.target.checked)for(let o of n.tickets)n.selectedIds.add(o.id);else n.selectedIds.clear();f()})}var U=null;function ze(){document.getElementById("detail-close").addEventListener("click",Q);let e=["detail-title","detail-details"];for(let i of e){let s=document.getElementById(i);s.addEventListener("input",()=>{U&&clearTimeout(U),U=setTimeout(()=>{if(n.activeTicketId==null)return;let a=i.replace("detail-","");c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:s.value}}).then(()=>{m()})},300)})}let t=["detail-category","detail-priority","detail-status"];for(let i of t){let s=document.getElementById(i);s.addEventListener("change",async()=>{if(n.activeTicketId==null)return;let a=i.replace("detail-","");await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:s.value}}),m()})}document.getElementById("detail-upnext").addEventListener("change",async()=>{if(n.activeTicketId==null)return;let i=n.tickets.find(a=>a.id===n.activeTicketId),s=document.getElementById("detail-upnext");if(s.checked&&i&&(i.status==="completed"||i.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?")){s.checked=!1;return}await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{status:"not_started",up_next:!0}})}else await c(`/tickets/${n.activeTicketId}/up-next`,{method:"POST"});m(),x(n.activeTicketId)}),document.getElementById("detail-file-input").addEventListener("change",async i=>{let s=i.target,a=s.files?.[0];!a||n.activeTicketId==null||(await G(`/tickets/${n.activeTicketId}/attachments`,a),s.value="",x(n.activeTicketId),m())}),document.getElementById("detail-attachments").addEventListener("click",async i=>{let a=i.target.closest(".attachment-delete");if(a===null)return;let o=a.dataset.attId;o===void 0||o===""||(await c(`/attachments/${o}`,{method:"DELETE"}),n.activeTicketId!=null&&x(n.activeTicketId))})}function je(e){if(!e||e==="")return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return e.trim()?[{text:e,created_at:""}]:[]}function Fe(e){let t=[];t.push(`${e.ticket_number}: ${e.title}`),e.details.trim()&&(t.push(""),t.push(e.details.trim()));let i=je(e.notes);if(i.length>0){t.push("");for(let s of i)t.push(`- ${s.text}`)}return t.join(`
|
|
2
|
-
`)}function
|
|
1
|
+
"use strict";(()=>{var ye=Object.defineProperty;var ge=(e,t,s)=>t in e?ye(e,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[t]=s;var J=(e,t,s)=>ge(e,typeof t!="symbol"?t+"":t,s);function G(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function Y(e){return e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">")}var h=class{constructor(t){J(this,"__html");this.__html=t}toString(){return this.__html}};function k(e){return new h(e)}var ve=new Set(["area","base","br","col","embed","hr","img","input","link","meta","source","track","wbr"]);function A(e){return e==null||typeof e=="boolean"?"":e instanceof h?e.__html:typeof e=="string"?G(e):typeof e=="number"?String(e):Array.isArray(e)?e.map(A).join(""):""}function he(e,t){if(t==null||t===!1)return"";if(t===!0)return` ${e}`;let s=e==="className"?"class":e==="htmlFor"?"for":e,i;return t instanceof h?i=t.__html:typeof t=="number"?i=String(t):typeof t=="string"?i=Y(t):i="",` ${s}="${i}"`}function l(e,t){if(typeof e=="function")return e(t);let{children:s,...i}=t,a=Object.entries(i).map(([d,o])=>he(d,o)).join("");if(ve.has(e))return new h(`<${e}${a}>`);let r=s!=null?A(s):"";return new h(`<${e}${a}>${r}</${e}>`)}function x({children:e}){return new h(e!=null?A(e):"")}function g(e){let t=document.createElement("template");return t.innerHTML=e.toString(),t.content.firstElementChild}function X(e){document.getElementById("network-error-popup")?.remove();let t=g(l("div",{id:"network-error-popup",className:"error-popup",children:l("div",{className:"error-popup-content",children:[l("strong",{children:"Connection Error"}),l("p",{children:e}),l("button",{children:k("Dismiss")})]})}));t.querySelector("button").addEventListener("click",()=>t.remove()),document.body.appendChild(t)}async function c(e,t={}){try{return(await fetch("/api"+e,{headers:t.body!==void 0?{"Content-Type":"application/json"}:{},method:t.method,body:t.body!==void 0?JSON.stringify(t.body):void 0})).json()}catch(s){throw X("Unable to reach the server. It may have been stopped."),s}}async function Q(e,t){try{let s=new FormData;return s.append("file",t),(await fetch("/api"+e,{method:"POST",body:s})).json()}catch(s){throw X("Unable to reach the server. It may have been stopped."),s}}var be={detail_position:"side",detail_width:360,detail_height:300,trash_cleanup_days:3,verified_cleanup_days:30},n={tickets:[],selectedIds:new Set,lastClickedId:null,activeTicketId:null,view:"all",layout:"list",sortBy:"created",sortDir:"desc",search:"",settings:{...be}},ke={issue:"#6b7280",bug:"#ef4444",feature:"#22c55e",requirement_change:"#f97316",task:"#3b82f6",investigation:"#8b5cf6"},Te={issue:"ISS",bug:"BUG",feature:"FEA",requirement_change:"REQ",task:"TSK",investigation:"INV"},Ee={highest:"\u2B06\u2B06",high:"\u2B06",default:"\u2014",low:"\u2B07",lowest:"\u2B07\u2B07"},Ie={highest:"#ef4444",high:"#f97316",default:"#6b7280",low:"#3b82f6",lowest:"#94a3b8"},Le={not_started:"\u25CB",started:"\u25D4",completed:"\u2713",verified:"svg"},Z='<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 7 17l-5-5"/><path d="m22 10-9.5 9.5-2-2"/></svg>';function b(e){return ke[e]||"#6b7280"}function I(e){return Te[e]||"ISS"}function P(e){return Ee[e]||"\u2014"}function _(e){return Ie[e]||"#6b7280"}function ee(e){return Le[e]||"\u25CB"}function C(e){n.activeTicketId=e,se(e)}function te(){n.selectedIds.clear(),n.activeTicketId=null;let e=new CustomEvent("hotsheet:render");document.dispatchEvent(e)}function ne(){let e=n.view==="trash",t=document.getElementById("detail-panel"),s=document.getElementById("detail-resize-handle");if(n.selectedIds.size===1&&!e){let i=Array.from(n.selectedIds)[0];t.style.display="flex",s&&(s.style.display=""),n.activeTicketId!==i&&(n.activeTicketId=i,se(i))}else n.activeTicketId!=null&&(n.activeTicketId=null),t.style.display="none",s&&(s.style.display="none")}async function se(e){let t=await c(`/tickets/${e}`);if(n.activeTicketId!==e)return;document.getElementById("detail-ticket-number").textContent=t.ticket_number,document.getElementById("detail-title").value=t.title,document.getElementById("detail-category").value=t.category,document.getElementById("detail-priority").value=t.priority,document.getElementById("detail-status").value=t.status,document.getElementById("detail-upnext").checked=t.up_next,document.getElementById("detail-details").value=t.details;let s=document.getElementById("detail-attachments");t.attachments.length>0?s.innerHTML=l(x,{children:t.attachments.map(o=>l("div",{className:"attachment-item",children:[l("span",{className:"attachment-name",children:o.original_filename}),l("button",{className:"attachment-delete","data-att-id":String(o.id),title:"Remove",children:k("×")})]}))}).toString():s.innerHTML="";let i=document.getElementById("detail-notes-section"),a=document.getElementById("detail-notes"),r=we(t.notes);r.length>0?(i.style.display="",a.innerHTML=l(x,{children:r.map(o=>l("div",{className:"note-entry",children:[o.created_at?l("div",{className:"note-timestamp",children:new Date(o.created_at).toLocaleString()}):null,l("div",{className:"note-text",children:o.text})]}))}).toString()):(i.style.display="none",a.innerHTML="");let d=document.getElementById("detail-meta");d.innerHTML=l(x,{children:[l("div",{children:["Created: ",new Date(t.created_at).toLocaleString()]}),l("div",{children:["Updated: ",new Date(t.updated_at).toLocaleString()]}),t.completed_at?l("div",{children:["Completed: ",new Date(t.completed_at).toLocaleString()]}):null,t.verified_at?l("div",{children:["Verified: ",new Date(t.verified_at).toLocaleString()]}):null]}).toString()}function we(e){if(!e||e==="")return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return e.trim()?[{text:e,created_at:""}]:[]}async function $(){try{let e=await c("/stats"),t=document.getElementById("status-bar");t&&(t.textContent=`${e.total} tickets \xB7 ${e.open} open \xB7 ${e.up_next} up next`)}catch{}}function O(e){let t=document.getElementById("content-area");t.classList.remove("detail-side","detail-bottom"),t.classList.add(e==="bottom"?"detail-bottom":"detail-side")}function R(){let e=document.getElementById("detail-panel");n.settings.detail_position==="bottom"?(e.style.width="",e.style.height=`${n.settings.detail_height}px`):(e.style.height="",e.style.width=`${n.settings.detail_width}px`)}function ie(){let e=document.getElementById("detail-resize-handle"),t=document.getElementById("detail-panel"),s=document.getElementById("content-area"),i=!1;e.addEventListener("mousedown",a=>{a.preventDefault(),i=!0,document.body.style.cursor=n.settings.detail_position==="bottom"?"row-resize":"col-resize",document.body.style.userSelect="none"}),document.addEventListener("mousemove",a=>{if(!i)return;let r=s.getBoundingClientRect();if(n.settings.detail_position==="bottom"){let d=Math.max(150,Math.min(500,r.bottom-a.clientY));n.settings.detail_height=d,t.style.height=`${d}px`}else{let d=Math.max(250,Math.min(600,r.right-a.clientX));n.settings.detail_width=d,t.style.width=`${d}px`}}),document.addEventListener("mouseup",()=>{i&&(i=!1,document.body.style.cursor="",document.body.style.userSelect="",n.settings.detail_position==="bottom"?c("/settings",{method:"PATCH",body:{detail_height:String(n.settings.detail_height)}}):c("/settings",{method:"PATCH",body:{detail_width:String(n.settings.detail_width)}}))})}function H(e,t){let s=t.getBoundingClientRect(),i=e.getBoundingClientRect(),a=window.innerWidth,r=window.innerHeight,d=s.left;d+i.width>a-8&&(d=s.right-i.width),d<8&&(d=8);let o=s.bottom+4;o+i.height>r-8&&(o=s.top-i.height-4),o<8&&(o=8),e.style.left=`${d}px`,e.style.top=`${o}px`}function M(e,t){let s=g(l("div",{className:"dropdown-menu",style:"visibility:hidden;top:0;left:0",children:t.map(o=>l("button",{className:`dropdown-item${o.active?" active":""}`,"data-key":o.key,children:[o.color?l("span",{className:"dropdown-dot",style:`background-color:${o.color}`}):null,l("span",{className:"dropdown-label",children:o.label}),o.shortcut?l("kbd",{className:"dropdown-kbd",children:o.shortcut}):null]}))}));s.querySelectorAll(".dropdown-item").forEach((o,m)=>{o.addEventListener("click",()=>{t[m].action(),s.remove()})});function a(o){let m=t.find(u=>o.key.toLowerCase()===u.key.toLowerCase());m?(o.preventDefault(),o.stopPropagation(),m.action(),r()):o.key==="Escape"&&(o.preventDefault(),r())}function r(){s.remove(),document.removeEventListener("keydown",a,!0),document.removeEventListener("click",d)}function d(){r()}return document.addEventListener("keydown",a,!0),setTimeout(()=>{document.addEventListener("click",d)},0),s}function B(){document.querySelectorAll(".dropdown-menu").forEach(e=>{e.remove()})}var q=null,L=!1,w=null,U="",N=[{key:"i",value:"issue",label:"Issue"},{key:"b",value:"bug",label:"Bug"},{key:"f",value:"feature",label:"Feature"},{key:"r",value:"requirement_change",label:"Req Change"},{key:"k",value:"task",label:"Task"},{key:"g",value:"investigation",label:"Investigation"}],K=[{key:"1",value:"highest",label:"Highest"},{key:"2",value:"high",label:"High"},{key:"3",value:"default",label:"Default"},{key:"4",value:"low",label:"Low"},{key:"5",value:"lowest",label:"Lowest"}];function Se(){let e=document.activeElement;if(!e||!(e instanceof HTMLElement))return null;let t=e.closest(".ticket-row");if(!t)return null;if(t.classList.contains("draft-row"))return"draft";let s=t.dataset.id;return s!==void 0&&s!==""?parseInt(s,10):null}function xe(e){e!=null&&(L=!0,e==="draft"?v():document.querySelector(`.ticket-row[data-id="${e}"] .ticket-title-input`)?.focus(),L=!1)}function D(){let e=n.view;return e!=="completed"&&e!=="verified"&&e!=="trash"&&e!=="up-next"&&e!=="open"}function _e(){return n.view==="open"?[{status:"not_started",label:"Not Started"},{status:"started",label:"Started"}]:n.view==="non-verified"?[{status:"not_started",label:"Not Started"},{status:"started",label:"Started"},{status:"completed",label:"Completed"}]:[{status:"not_started",label:"Not Started"},{status:"started",label:"Started"},{status:"completed",label:"Completed"},{status:"verified",label:"Verified"}]}function f(){if(n.layout==="columns"&&D()){Ce();return}let e=n.view==="trash",t=Se(),s=null;if(t!=null&&t!=="draft"){let a=document.querySelector(`.ticket-row[data-id="${t}"] .ticket-title-input`);a&&(s=a.value)}let i=document.getElementById("ticket-list");i.innerHTML="",i.classList.remove("ticket-list-columns"),e||i.appendChild(re()),e&&n.tickets.length===0&&i.appendChild(g(l("div",{className:"ticket-list-empty",children:"Trash is empty"})));for(let a of n.tickets)i.appendChild(e?Ae(a):De(a));if(t!=null&&t!=="draft"&&s!=null){let a=document.querySelector(`.ticket-row[data-id="${t}"] .ticket-title-input`);a&&a.value!==s&&(a.value=s)}xe(t),T(),$()}function Ce(){let e=document.getElementById("ticket-list");e.innerHTML="",e.classList.add("ticket-list-columns"),e.appendChild(re());let t=_e(),s=g(l("div",{className:"columns-container"}));for(let i of t){let a=n.tickets.filter(o=>o.status===i.status),r=g(l("div",{className:"column","data-status":i.status,children:[l("div",{className:"column-header",children:[l("span",{className:"column-title",children:i.label}),l("span",{className:"column-count",children:String(a.length)})]}),l("div",{className:"column-body"})]})),d=r.querySelector(".column-body");for(let o of a)d.appendChild(He(o));d.addEventListener("dragover",o=>{o.preventDefault(),o.dataTransfer.dropEffect="move",r.classList.add("column-drop-target")}),d.addEventListener("dragleave",o=>{let m=o.relatedTarget;(!m||!d.contains(m))&&r.classList.remove("column-drop-target")}),d.addEventListener("drop",o=>{o.preventDefault(),r.classList.remove("column-drop-target");let m=o.dataTransfer.getData("application/hotsheet-tickets");if(!m)return;let u=JSON.parse(m);c("/tickets/batch",{method:"POST",body:{ids:u,action:"status",value:i.status}}).then(()=>{p()})}),s.appendChild(r)}e.appendChild(s),T(),$()}function He(e){let t=n.selectedIds.has(e.id),s=g(l("div",{className:`column-card${t?" selected":""}${e.up_next?" up-next":""}`,"data-id":String(e.id),children:[l("div",{className:"column-card-header",children:[l("span",{className:"ticket-category-badge",style:`background-color:${b(e.category)}`,children:I(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("span",{className:"ticket-priority-indicator",style:`color:${_(e.priority)}`,children:P(e.priority)}),l("button",{className:`ticket-star${e.up_next?" active":""}`,title:e.up_next?"Remove from Up Next":"Add to Up Next",children:e.up_next?"\u2605":"\u2606"})]}),l("div",{className:"column-card-title",children:e.title})]})),i=s.querySelector(".ticket-priority-indicator");return i.addEventListener("click",a=>{a.stopPropagation(),ue(i,e)}),s.querySelector(".ticket-star").addEventListener("click",a=>{a.stopPropagation(),ce(e)}),s.draggable=!0,s.addEventListener("dragstart",a=>{let r;n.selectedIds.has(e.id)&&n.selectedIds.size>1?r=Array.from(n.selectedIds):r=[e.id],a.dataTransfer.setData("application/hotsheet-tickets",JSON.stringify(r)),a.dataTransfer.effectAllowed="move"}),s.addEventListener("click",a=>{if(a.metaKey||a.ctrlKey)n.selectedIds.has(e.id)?n.selectedIds.delete(e.id):n.selectedIds.add(e.id),n.lastClickedId=e.id;else if(a.shiftKey&&n.lastClickedId!=null){let r=n.tickets.map(m=>m.id),d=r.indexOf(n.lastClickedId),o=r.indexOf(e.id);if(d!==-1&&o!==-1){let m=Math.min(d,o),u=Math.max(d,o);n.selectedIds.clear();for(let y=m;y<=u;y++)n.selectedIds.add(r[y])}}else n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id;Me(),T()}),s}function Me(){document.querySelectorAll(".column-card[data-id]").forEach(e=>{let t=parseInt(e.dataset.id,10);n.selectedIds.has(t)?e.classList.add("selected"):e.classList.remove("selected")})}function re(){let e=le(),t=n.view.startsWith("category:"),s=g(l("div",{className:"ticket-row draft-row",children:[l("span",{className:"ticket-checkbox-spacer"}),l("span",{className:"ticket-status-btn draft-placeholder",children:"\u25CB"}),l("span",{className:"ticket-category-badge draft-badge",style:`background-color:${b(e)}${t?"":";cursor:pointer;opacity:1"}`,children:I(e)}),l("span",{className:"ticket-number draft-number"}),l("input",{type:"text",className:"ticket-title-input draft-input",placeholder:"New ticket...",value:U}),l("span",{className:"ticket-priority-indicator draft-placeholder"}),l("span",{className:"ticket-star draft-placeholder"})]}));if(!t){let a=s.querySelector(".ticket-category-badge");a.addEventListener("click",r=>{r.stopPropagation(),Ne(a)})}let i=s.querySelector(".draft-input");return i.addEventListener("input",()=>{U=i.value}),i.addEventListener("keydown",async a=>{if(a.key==="Enter"&&i.value.trim()){a.preventDefault();let r=i.value.trim();U="",i.value="";let d=Be();w&&!n.view.startsWith("category:")&&(d.category=w);let o=await c("/tickets",{method:"POST",body:{title:r,defaults:d}});o&&(n.selectedIds.clear(),n.selectedIds.add(o.id)),await p(),v()}else a.key==="ArrowDown"&&(a.preventDefault(),n.tickets.length>0&&document.querySelector(`.ticket-row[data-id="${n.tickets[0].id}"] .ticket-title-input`)?.focus())}),s}function v(){document.querySelector(".draft-row .draft-input")?.focus()}function Be(){let e=n.view;return e==="up-next"?{up_next:!0}:e==="open"?{}:e==="completed"?{status:"completed"}:e.startsWith("category:")?{category:e.split(":")[1]}:e.startsWith("priority:")?{priority:e.split(":")[1]}:{}}function le(){if(w)return w;let e=n.view;return e.startsWith("category:")?e.split(":")[1]:"issue"}function Ne(e){B();let s=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",i=le(),a=M(e,N.map(r=>({label:r.label,key:r.key,shortcut:`${s}${r.key.toUpperCase()}`,color:b(r.value),active:i===r.value,action:()=>{w=r.value,f(),v()}})));document.body.appendChild(a),H(a,e),a.style.visibility=""}function De(e){let t=n.selectedIds.has(e.id),s=e.status==="completed"||e.status==="verified",i=e.status==="verified",a=g(l("div",{className:`ticket-row${t?" selected":""}${s?" completed":""}${e.up_next?" up-next":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("button",{className:`ticket-status-btn${i?" verified":""}`,title:e.status.replace("_"," "),children:i?k(Z):ee(e.status)}),l("span",{className:"ticket-category-badge",style:`background-color:${b(e.category)}`,title:e.category,children:I(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("input",{type:"text",className:"ticket-title-input",value:e.title}),l("span",{className:"ticket-priority-indicator",style:`color:${_(e.priority)}`,title:e.priority,children:P(e.priority)}),l("button",{className:`ticket-star${e.up_next?" active":""}`,title:e.up_next?"Remove from Up Next":"Add to Up Next",children:e.up_next?"\u2605":"\u2606"})]}));a.addEventListener("mousedown",u=>{let y=u.target;y.tagName!=="INPUT"&&y.tagName!=="BUTTON"&&(a.draggable=!0)}),a.addEventListener("mouseup",()=>{a.draggable=!1}),a.addEventListener("dragend",()=>{a.draggable=!1}),a.addEventListener("dragstart",u=>{let y;n.selectedIds.has(e.id)&&n.selectedIds.size>1?y=Array.from(n.selectedIds):y=[e.id],u.dataTransfer.setData("application/hotsheet-tickets",JSON.stringify(y)),u.dataTransfer.effectAllowed="move"}),a.addEventListener("mousedown",u=>{(u.metaKey||u.ctrlKey||u.shiftKey)&&(u.preventDefault(),de(u,e)&&u.stopPropagation())});let r=a.querySelector(".ticket-checkbox");r.addEventListener("click",u=>u.stopPropagation()),r.addEventListener("change",()=>{r.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,f()}),a.querySelector(".ticket-status-btn").addEventListener("click",u=>{u.stopPropagation(),Re(e)});let d=a.querySelector(".ticket-category-badge");d.addEventListener("click",u=>{u.stopPropagation(),Ke(d,e)});let o=a.querySelector(".ticket-title-input");o.addEventListener("focus",()=>{L||n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,z(),T())}),o.addEventListener("input",()=>{Ue(e.id,{title:o.value})}),o.addEventListener("keydown",u=>{Pe(u,e,o)});let m=a.querySelector(".ticket-priority-indicator");return m.addEventListener("click",u=>{u.stopPropagation(),ue(m,e)}),a.querySelector(".ticket-star").addEventListener("click",u=>{u.stopPropagation(),ce(e)}),a}function Ae(e){let t=n.selectedIds.has(e.id),s=e.deleted_at?new Date(e.deleted_at):null,i=g(l("div",{className:`ticket-row trash-row${t?" selected":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("span",{className:"ticket-category-badge",style:`background-color:${b(e.category)}`,children:I(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("span",{className:"ticket-title-input trash-title",style:"cursor:default",children:e.title}),l("span",{className:"ticket-number",title:s?`Deleted: ${s.toLocaleString()}`:"",children:s?s.toLocaleDateString():""}),l("button",{className:"btn btn-sm",title:"Restore from trash",children:"Restore"})]}));i.addEventListener("mousedown",r=>{(r.metaKey||r.ctrlKey||r.shiftKey)&&(r.preventDefault(),de(r,e)&&r.stopPropagation())});let a=i.querySelector(".ticket-checkbox");return a.addEventListener("click",r=>r.stopPropagation()),a.addEventListener("change",()=>{a.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,f()}),i.querySelector(".trash-title").addEventListener("click",()=>{n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,z(),T())}),i.querySelector(".btn").addEventListener("click",async r=>{r.stopPropagation(),await c(`/tickets/${e.id}/restore`,{method:"POST"}),p()}),i}function de(e,t){let s=e.metaKey||e.ctrlKey,i=e.shiftKey;if(s)n.selectedIds.has(t.id)?n.selectedIds.delete(t.id):n.selectedIds.add(t.id),n.lastClickedId=t.id,f();else if(i&&n.lastClickedId!=null){let a=n.tickets.map(o=>o.id),r=a.indexOf(n.lastClickedId),d=a.indexOf(t.id);if(r!==-1&&d!==-1){let o=Math.min(r,d),m=Math.max(r,d);n.selectedIds.clear();for(let u=o;u<=m;u++)n.selectedIds.add(a[u])}f()}else return!1;return!0}function Pe(e,t,s){if(e.key==="Enter")e.preventDefault(),v();else if(e.key==="Backspace"&&s.value==="")e.preventDefault(),qe(t.id);else if(e.key==="ArrowDown"&&e.shiftKey)e.preventDefault(),ae(t.id,1);else if(e.key==="ArrowUp"&&e.shiftKey)e.preventDefault(),ae(t.id,-1);else if(e.key==="ArrowDown")e.preventDefault(),$e(t.id);else if(e.key==="ArrowUp")e.preventDefault(),Oe(t.id);else if((e.metaKey||e.ctrlKey)&&!e.altKey&&N.some(i=>i.key===e.key)){e.preventDefault();let i=N.find(a=>a.key===e.key);oe(t,"category",i.value)}else if(e.altKey&&!e.metaKey&&!e.ctrlKey&&K.some(i=>i.key===e.key)){e.preventDefault();let i=K.find(a=>a.key===e.key);oe(t,"priority",i.value)}}function $e(e){let t=n.tickets.findIndex(s=>s.id===e);t<n.tickets.length-1&&document.querySelector(`.ticket-row[data-id="${n.tickets[t+1].id}"] .ticket-title-input`)?.focus()}function Oe(e){let t=n.tickets.findIndex(s=>s.id===e);t>0?document.querySelector(`.ticket-row[data-id="${n.tickets[t-1].id}"] .ticket-title-input`)?.focus():v()}function ae(e,t){let i=n.tickets.findIndex(d=>d.id===e)+t;if(i<0||i>=n.tickets.length)return;let a=n.tickets[i].id;n.selectedIds.add(e),n.selectedIds.has(a)?n.selectedIds.delete(e):n.selectedIds.add(a),L=!0,document.querySelector(`.ticket-row[data-id="${a}"] .ticket-title-input`)?.focus(),L=!1,z(),T()}async function Re(e){let s={not_started:"started",started:"completed",completed:"verified",verified:"not_started"}[e.status]||"not_started",i=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:s}});Object.assign(e,i),f()}async function ce(e){if(!e.up_next&&(e.status==="completed"||e.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?"))return;let s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:"not_started",up_next:!0}});Object.assign(e,s),f();return}let t=await c(`/tickets/${e.id}/up-next`,{method:"POST"});Object.assign(e,t),f()}async function oe(e,t,s){let i=await c(`/tickets/${e.id}`,{method:"PATCH",body:{[t]:s}});Object.assign(e,i),f()}async function qe(e){let t=n.tickets.findIndex(s=>s.id===e);if(await c(`/tickets/${e}`,{method:"DELETE"}),n.tickets=n.tickets.filter(s=>s.id!==e),n.selectedIds.delete(e),f(),t>0&&n.tickets.length>0){let s=Math.min(t-1,n.tickets.length-1);document.querySelector(`.ticket-row[data-id="${n.tickets[s].id}"] .ticket-title-input`)?.focus()}else v()}function Ue(e,t){q&&clearTimeout(q),q=setTimeout(()=>{c(`/tickets/${e}`,{method:"PATCH",body:t})},300)}function Ke(e,t){B();let i=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",a=M(e,N.map(r=>({label:r.label,key:r.key,shortcut:`${i}${r.key.toUpperCase()}`,color:b(r.value),active:t.category===r.value,action:async()=>{let d=await c(`/tickets/${t.id}`,{method:"PATCH",body:{category:r.value}});Object.assign(t,d),f()}})));document.body.appendChild(a),H(a,e),a.style.visibility=""}function ue(e,t){B();let s=M(e,K.map(i=>({label:i.label,key:i.key,shortcut:`Alt+${i.key}`,color:_(i.value),active:t.priority===i.value,action:async()=>{let a=await c(`/tickets/${t.id}`,{method:"PATCH",body:{priority:i.value}});Object.assign(t,a),f()}})));document.body.appendChild(s),H(s,e),s.style.visibility=""}function z(){document.querySelectorAll(".ticket-row[data-id]").forEach(e=>{let t=parseInt(e.dataset.id,10),s=e.querySelector(".ticket-checkbox");n.selectedIds.has(t)?(e.classList.add("selected"),s&&(s.checked=!0)):(e.classList.remove("selected"),s&&(s.checked=!1))})}function T(){let e=n.selectedIds.size,t=n.tickets.length,s=e>0,i=n.view==="trash",a=document.getElementById("batch-select-all");a.checked=t>0&&e===t,a.indeterminate=e>0&&e<t,document.getElementById("batch-count").textContent=s?`${e} selected`:"";let r=["batch-category","batch-priority","batch-status","batch-upnext","batch-delete"];for(let y of r){let S=document.getElementById(y);S.style.display=i?"none":"",i||(S.disabled=!s)}let d=document.getElementById("batch-restore"),o=document.getElementById("batch-empty-trash");if(i){let y=document.getElementById("batch-toolbar");d||(d=g(l("button",{id:"batch-restore",className:"btn btn-sm",children:"Restore"})),d.addEventListener("click",async()=>{await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"restore"}}),n.selectedIds.clear(),p()}),y.insertBefore(d,document.getElementById("batch-count"))),d.disabled=!s,d.style.display="",o||(o=g(l("button",{id:"batch-empty-trash",className:"btn btn-sm btn-danger",children:"Empty Trash"})),o.addEventListener("click",async()=>{confirm("Permanently delete all items in trash? This cannot be undone.")&&(await c("/trash/empty",{method:"POST"}),n.selectedIds.clear(),p())}),y.insertBefore(o,document.getElementById("batch-count"))),o.disabled=t===0,o.style.display=""}else d&&(d.style.display="none"),o&&(o.style.display="none");let m=document.querySelector(".batch-star-icon"),u=document.getElementById("batch-upnext");if(!i&&m&&s){let y=n.tickets.filter(E=>n.selectedIds.has(E.id)),S=y.every(E=>E.up_next),fe=y.every(E=>!E.up_next);S?(m.textContent="\u2605",u.classList.add("active"),u.classList.remove("mixed")):fe?(m.textContent="\u2606",u.classList.remove("active","mixed")):(m.innerHTML=l("span",{className:"star-mixed-wrap",children:[l("span",{className:"star-mixed-fill",children:"\u2605"}),"\u2606"]}).toString(),u.classList.remove("active"),u.classList.add("mixed"))}else m&&(m.textContent="\u2606",u.classList.remove("active","mixed"));ne()}async function p(){let e=new URLSearchParams;n.view==="trash"?e.set("status","deleted"):n.view==="up-next"?e.set("up_next","true"):n.view==="open"?e.set("status","open"):n.view==="completed"?e.set("status","completed"):n.view==="non-verified"?e.set("status","non_verified"):n.view==="verified"?e.set("status","verified"):n.view.startsWith("category:")?e.set("category",n.view.split(":")[1]):n.view.startsWith("priority:")&&e.set("priority",n.view.split(":")[1]),n.search&&e.set("search",n.search),e.set("sort_by",n.sortBy),e.set("sort_dir",n.sortDir);let t=e.toString();n.tickets=await c(`/tickets${t?"?"+t:""}`),f()}async function ze(){await Ve(),await p(),Ye(),We(),Je(),Xe(),Qe(),Ze(),et(),st(),Fe(),je(),ie(),it(),document.addEventListener("hotsheet:render",()=>f()),v()}async function Ve(){try{let e=await c("/settings");(e.detail_position==="side"||e.detail_position==="bottom")&&(n.settings.detail_position=e.detail_position),e.detail_width&&(n.settings.detail_width=parseInt(e.detail_width,10)||360),e.detail_height&&(n.settings.detail_height=parseInt(e.detail_height,10)||300),e.trash_cleanup_days&&(n.settings.trash_cleanup_days=parseInt(e.trash_cleanup_days,10)||3),e.verified_cleanup_days&&(n.settings.verified_cleanup_days=parseInt(e.verified_cleanup_days,10)||30),(e.layout==="list"||e.layout==="columns")&&(n.layout=e.layout)}catch{}O(n.settings.detail_position),R()}function Fe(){let e=document.getElementById("settings-overlay"),t=document.getElementById("settings-close");document.getElementById("settings-btn").addEventListener("click",()=>{document.getElementById("settings-trash-days").value=String(n.settings.trash_cleanup_days),document.getElementById("settings-verified-days").value=String(n.settings.verified_cleanup_days),e.style.display="flex"}),t.addEventListener("click",()=>{e.style.display="none"}),e.addEventListener("click",o=>{o.target===e&&(e.style.display="none")});let i=document.getElementById("settings-trash-days"),a=null;i.addEventListener("input",()=>{a&&clearTimeout(a),a=setTimeout(()=>{let o=Math.max(1,parseInt(i.value,10)||3);i.value=String(o),n.settings.trash_cleanup_days=o,c("/settings",{method:"PATCH",body:{trash_cleanup_days:String(o)}})},500)});let r=document.getElementById("settings-verified-days"),d=null;r.addEventListener("input",()=>{d&&clearTimeout(d),d=setTimeout(()=>{let o=Math.max(1,parseInt(r.value,10)||30);r.value=String(o),n.settings.verified_cleanup_days=o,c("/settings",{method:"PATCH",body:{verified_cleanup_days:String(o)}})},500)})}function je(){let e=document.getElementById("copy-prompt-section"),t=document.getElementById("copy-prompt-btn"),s=document.getElementById("copy-prompt-label"),i=document.getElementById("copy-prompt-icon"),a="";c("/worklist-info").then(r=>{a=r.prompt,e.style.display="",r.skillCreated&&console.log("Hot Sheet: Created /hotsheet skill in .claude/skills/hotsheet/")}),t.addEventListener("click",()=>{a!==""&&navigator.clipboard.writeText(a).then(()=>{s.textContent="Copied!",i.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>',setTimeout(()=>{s.textContent="Copy AI prompt",i.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>'},1500)})})}function W(){let e=document.getElementById("layout-toggle"),t=D(),s=e.querySelector('[data-layout="columns"]');s.disabled=!t,s.style.opacity=t?"":"0.3";let i=n.layout==="columns"&&!t?"list":n.layout;e.querySelectorAll(".layout-btn").forEach(a=>{a.classList.toggle("active",a.dataset.layout===i)})}function We(){document.getElementById("layout-toggle").querySelectorAll(".layout-btn").forEach(t=>{t.addEventListener("click",()=>{let s=t.dataset.layout;s==="columns"&&!D()||(n.layout=s,W(),f(),c("/settings",{method:"PATCH",body:{layout:s}}))})}),W()}function me(){document.getElementById("detail-position-toggle").querySelectorAll(".layout-btn").forEach(t=>{t.classList.toggle("active",t.dataset.position===n.settings.detail_position)})}function Je(){document.getElementById("detail-position-toggle").querySelectorAll(".layout-btn").forEach(t=>{t.addEventListener("click",()=>{let s=t.dataset.position;n.settings.detail_position=s,O(s),R(),me(),c("/settings",{method:"PATCH",body:{detail_position:s}})})}),me()}function pe(e){return e==="up-next"?{action:"up_next",value:!0}:e==="open"?{action:"status",value:"not_started"}:e==="completed"?{action:"status",value:"completed"}:e==="verified"?{action:"status",value:"verified"}:e==="trash"?{action:"delete",value:null}:e.startsWith("category:")?{action:"category",value:e.split(":")[1]}:e.startsWith("priority:")?{action:"priority",value:e.split(":")[1]}:null}async function Ge(e,t){let s=pe(e);s&&(s.action==="delete"?await c("/tickets/batch",{method:"POST",body:{ids:t,action:"delete"}}):await c("/tickets/batch",{method:"POST",body:{ids:t,action:s.action,value:s.value}}),p())}function Ye(){let e=document.querySelectorAll(".sidebar-item[data-view]");e.forEach(t=>{t.addEventListener("click",()=>{e.forEach(i=>{i.classList.remove("active")}),t.classList.add("active"),n.view=t.dataset.view,n.selectedIds.clear(),W(),p()});let s=t.dataset.view;pe(s)&&(t.addEventListener("dragover",i=>{i.preventDefault(),i.dataTransfer.dropEffect="move",t.classList.add("drop-target")}),t.addEventListener("dragleave",()=>{t.classList.remove("drop-target")}),t.addEventListener("drop",i=>{i.preventDefault(),t.classList.remove("drop-target");let a=i.dataTransfer.getData("application/hotsheet-tickets");if(!a)return;let r=JSON.parse(a);Ge(s,r)}))})}function Xe(){let e=document.getElementById("sort-select");e.addEventListener("change",()=>{let[t,s]=e.value.split(":");n.sortBy=t,n.sortDir=s,p()})}var V=null;function Qe(){let e=document.getElementById("search-input");e.addEventListener("input",()=>{V&&clearTimeout(V),V=setTimeout(()=>{n.search=e.value,p()},200)}),e.addEventListener("keydown",t=>{t.key==="Escape"&&(e.value="",n.search="",p())})}function Ze(){let e=document.getElementById("batch-category");e.addEventListener("change",async()=>{e.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"category",value:e.value}}),e.value="",p())});let t=document.getElementById("batch-priority");t.addEventListener("change",async()=>{t.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"priority",value:t.value}}),t.value="",p())});let s=document.getElementById("batch-status");s.addEventListener("change",async()=>{s.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"status",value:s.value}}),s.value="",p())}),document.getElementById("batch-upnext").addEventListener("click",async()=>{let i=n.tickets.filter(d=>n.selectedIds.has(d.id)),r=!i.every(d=>d.up_next);if(r){let d=i.filter(o=>o.status==="completed"||o.status==="verified");if(d.length>0){if(!confirm("Some selected tickets are already done. Would you like to reopen them and add them to Up Next?"))return;await c("/tickets/batch",{method:"POST",body:{ids:d.map(o=>o.id),action:"status",value:"not_started"}})}}await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:r}}),p()}),document.getElementById("batch-delete").addEventListener("click",async()=>{let i=n.selectedIds.size;confirm(`Delete ${i} ticket(s)?`)&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"delete"}}),n.selectedIds.clear(),p())}),document.getElementById("batch-select-all").addEventListener("change",i=>{if(i.target.checked)for(let r of n.tickets)n.selectedIds.add(r.id);else n.selectedIds.clear();f()})}var F=null;function et(){document.getElementById("detail-close").addEventListener("click",te);let e=["detail-title","detail-details"];for(let s of e){let i=document.getElementById(s);i.addEventListener("input",()=>{F&&clearTimeout(F),F=setTimeout(()=>{if(n.activeTicketId==null)return;let a=s.replace("detail-","");c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:i.value}}).then(()=>{p()})},300)})}let t=["detail-category","detail-priority","detail-status"];for(let s of t){let i=document.getElementById(s);i.addEventListener("change",async()=>{if(n.activeTicketId==null)return;let a=s.replace("detail-","");await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:i.value}}),p()})}document.getElementById("detail-upnext").addEventListener("change",async()=>{if(n.activeTicketId==null)return;let s=n.tickets.find(a=>a.id===n.activeTicketId),i=document.getElementById("detail-upnext");if(i.checked&&s&&(s.status==="completed"||s.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?")){i.checked=!1;return}await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{status:"not_started",up_next:!0}})}else await c(`/tickets/${n.activeTicketId}/up-next`,{method:"POST"});p(),C(n.activeTicketId)}),document.getElementById("detail-file-input").addEventListener("change",async s=>{let i=s.target,a=i.files?.[0];!a||n.activeTicketId==null||(await Q(`/tickets/${n.activeTicketId}/attachments`,a),i.value="",C(n.activeTicketId),p())}),document.getElementById("detail-attachments").addEventListener("click",async s=>{let a=s.target.closest(".attachment-delete");if(a===null)return;let r=a.dataset.attId;r===void 0||r===""||(await c(`/attachments/${r}`,{method:"DELETE"}),n.activeTicketId!=null&&C(n.activeTicketId))})}function tt(e){if(!e||e==="")return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return e.trim()?[{text:e,created_at:""}]:[]}function nt(e){let t=[];t.push(`${e.ticket_number}: ${e.title}`),e.details.trim()&&(t.push(""),t.push(e.details.trim()));let s=tt(e.notes);if(s.length>0){t.push("");for(let i of s)t.push(`- ${i.text}`)}return t.join(`
|
|
2
|
+
`)}function st(){document.addEventListener("keydown",e=>{let t=e.target.tagName,s=t==="INPUT"||t==="TEXTAREA"||t==="SELECT",i=document.getElementById("settings-overlay");if(e.key==="Escape"&&i.style.display!=="none"){i.style.display="none";return}if(e.key==="Escape"){n.selectedIds.size>0&&(n.selectedIds.clear(),f());return}if((e.metaKey||e.ctrlKey)&&e.key==="a"&&!s){e.preventDefault(),n.selectedIds.clear();for(let a of n.tickets)n.selectedIds.add(a.id);f();return}if((e.metaKey||e.ctrlKey)&&e.key==="d"){if(n.selectedIds.size>0){e.preventDefault();let a=n.tickets.filter(o=>n.selectedIds.has(o.id)),d=!a.every(o=>o.up_next);if(d){let o=a.filter(m=>m.status==="completed"||m.status==="verified");if(o.length>0){if(!confirm("Some selected tickets are already done. Would you like to reopen them and add them to Up Next?"))return;c("/tickets/batch",{method:"POST",body:{ids:o.map(m=>m.id),action:"status",value:"not_started"}}).then(()=>c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:!0}})).then(()=>{p()});return}}c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:d}}).then(()=>{p()})}return}if((e.metaKey||e.ctrlKey)&&e.key==="c"&&n.selectedIds.size>0){let a=window.getSelection();if(!a||a.isCollapsed||a.toString().trim()===""){e.preventDefault();let d=n.tickets.filter(o=>n.selectedIds.has(o.id)).map(nt).join(`
|
|
3
3
|
|
|
4
|
-
`);navigator.clipboard.writeText(d);return}}if((e.metaKey||e.ctrlKey)&&e.key==="n"){e.preventDefault(),
|
|
4
|
+
`);navigator.clipboard.writeText(d);return}}if((e.metaKey||e.ctrlKey)&&e.key==="n"){e.preventDefault(),v();return}if((e.metaKey||e.ctrlKey)&&e.key==="f"){e.preventDefault(),document.getElementById("search-input").focus();return}if(e.key==="n"&&!s){e.preventDefault(),v();return}})}var j=0;function it(){async function e(){try{let t=await c(`/poll?version=${j}`);t.version>j&&(j=t.version,p())}catch{await new Promise(t=>setTimeout(t,5e3))}setTimeout(e,100)}e()}ze();})();
|
package/dist/client/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}:root{--bg: #ffffff;--bg-secondary: #f9fafb;--bg-hover: #f3f4f6;--bg-selected: #eff6ff;--border: #e5e7eb;--text: #111827;--text-secondary: #6b7280;--text-muted: #9ca3af;--accent: #3b82f6;--accent-hover: #2563eb;--danger: #ef4444;--star: #eab308;--radius: 6px;--shadow: 0 1px 3px rgba(0,0,0,0.08)}html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-size:14px;line-height:1.5;color:var(--text);background:var(--bg)}button{cursor:pointer;border:none;background:none;font:inherit;color:inherit}input,select,textarea{font:inherit;color:inherit}.app{display:flex;flex-direction:column;height:100vh}.app-header{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-bottom:1px solid var(--border);background:var(--bg);gap:16px;flex-shrink:0}.app-header h1{font-size:18px;font-weight:700;white-space:nowrap}.header-controls{display:flex;align-items:center;gap:12px;flex:1;max-width:500px}.search-box{flex:1}.search-box input{width:100%;padding:6px 12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-secondary);outline:none}.search-box input:focus{border-color:var(--accent);background:var(--bg)}.sort-controls select{padding:6px 8px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);cursor:pointer}.settings-btn{font-size:18px;color:var(--text-muted);padding:4px;border-radius:var(--radius);flex-shrink:0}.settings-btn:hover{color:var(--text);background:var(--bg-hover)}.app-body{display:flex;flex:1;overflow:hidden}.content-area{flex:1;display:flex;overflow:hidden;min-width:0}.content-area.detail-side{flex-direction:row}.content-area.detail-bottom{flex-direction:column}.sidebar{width:180px;border-right:1px solid var(--border);background:var(--bg-secondary);overflow-y:auto;flex-shrink:0;padding:8px 0}.sidebar-copy-prompt{padding:8px 10px 4px}.copy-prompt-btn{display:flex;align-items:center;gap:6px;width:100%;padding:6px 8px;font-size:12px;color:var(--text-secondary);background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;transition:all .15s}.copy-prompt-btn:hover{color:var(--accent);border-color:var(--accent);background:var(--bg-selected)}.copy-prompt-icon{display:flex;align-items:center;flex-shrink:0}.sidebar-section{margin-bottom:8px}.sidebar-label{padding:8px 16px 4px;font-size:11px;font-weight:600;text-transform:uppercase;color:var(--text-muted);letter-spacing:.5px}.sidebar-item{display:flex;align-items:center;gap:8px;width:100%;padding:6px 16px;text-align:left;font-size:13px;color:var(--text-secondary);border-radius:0}.sidebar-item:hover{background:var(--bg-hover);color:var(--text)}.sidebar-item.active{background:var(--bg-selected);color:var(--accent);font-weight:500}.cat-dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}.sidebar-stats{padding:12px 16px;font-size:12px;color:var(--text-muted);border-top:1px solid var(--border);margin-top:8px}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0;min-height:0}.batch-toolbar{display:flex;align-items:center;gap:8px;padding:6px 16px 6px 19px;background:var(--bg-secondary);border-bottom:1px solid var(--border);flex-shrink:0;flex-wrap:wrap;min-height:36px}.batch-toolbar select:disabled,.batch-toolbar .btn:disabled{opacity:.4;cursor:default;pointer-events:none}.batch-toolbar select{padding:4px 8px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);font-size:12px}.batch-select-all{width:14px;height:14px;cursor:pointer;accent-color:var(--accent);flex-shrink:0}.batch-star-btn{font-size:16px;color:var(--text-muted);padding:2px 6px;border-radius:var(--radius);line-height:1}.batch-star-btn:hover:not(:disabled){color:var(--star);background:var(--bg-hover)}.batch-star-btn.active{color:var(--star)}.batch-star-btn.mixed{color:var(--star)}.batch-star-btn:disabled{opacity:.4;cursor:default;pointer-events:none}.star-mixed-wrap{position:relative;display:inline-block}.star-mixed-fill{position:absolute;left:0;top:0;overflow:hidden;width:50%}.batch-delete-btn.btn{display:inline-flex;align-items:center;justify-content:center;padding:4px 6px;border:none;background:none}.batch-count{font-size:12px;color:var(--text-muted);margin-left:auto;white-space:nowrap}.btn{padding:4px 10px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);font-size:12px;cursor:pointer}.btn:hover{background:var(--bg-hover)}.btn-sm{padding:3px 8px;font-size:11px}.btn-danger{color:var(--danger)}.btn-danger:hover:not(:disabled){background:#fef2f2}.ticket-list{flex:1;overflow-y:auto;padding:4px 0}.ticket-list-empty,.ticket-list-loading{padding:40px 20px;text-align:center;color:var(--text-muted);font-size:14px}.ticket-row{display:flex;align-items:center;gap:8px;padding:4px 16px;border-bottom:1px solid rgba(0,0,0,0);border-left:3px solid rgba(0,0,0,0);min-height:36px}.ticket-row:hover{background:var(--bg-hover)}.ticket-row.selected{background:var(--bg-selected)}.ticket-row.completed .ticket-title-input{text-decoration:line-through;color:var(--text-muted)}.ticket-row.up-next{border-left:3px solid var(--star)}.ticket-checkbox{flex-shrink:0;width:14px;height:14px;cursor:pointer;accent-color:var(--accent)}.ticket-status-btn{flex-shrink:0;width:20px;height:20px;font-size:14px;display:flex;align-items:center;justify-content:center;border-radius:50%;color:var(--text-secondary)}.ticket-status-btn:hover{background:var(--bg-hover)}.ticket-status-btn.verified{color:#22c55e}.ticket-category-badge{flex-shrink:0;width:4em;padding:1px 0;border-radius:3px;font-size:10px;font-weight:600;color:#fff;text-transform:uppercase;text-align:center;cursor:pointer;letter-spacing:.3px}.ticket-category-badge:hover{opacity:.85}.ticket-number{flex-shrink:0;font-size:11px;color:var(--text-muted);font-family:"SF Mono",Monaco,"Cascadia Code",monospace;min-width:5em}.ticket-title-input{flex:1;border:none;background:rgba(0,0,0,0);padding:2px 0;outline:none;min-width:0}.ticket-title-input:focus{border-bottom:1px solid var(--accent)}.ticket-title-input::placeholder{color:var(--text-muted)}.ticket-priority-indicator{flex-shrink:0;font-size:12px;cursor:pointer;padding:2px 4px;border-radius:3px;min-width:24px;text-align:center}.ticket-priority-indicator:hover{background:var(--bg-hover)}.ticket-star{flex-shrink:0;font-size:16px;color:var(--text-muted);padding:0}.ticket-star.active{color:var(--star)}.ticket-star:hover{color:var(--star)}.trash-row .trash-title{flex:1;color:var(--text-muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trash-row .btn{flex-shrink:0}.draft-row{border-bottom:1px dashed var(--border) !important;background:var(--bg-secondary)}.draft-row:hover{background:var(--bg-secondary)}.draft-row .draft-placeholder{opacity:.35;pointer-events:none}.draft-row .draft-badge{opacity:.35}.draft-row .draft-input::placeholder{color:var(--text-muted);font-style:italic}.ticket-checkbox-spacer{width:14px;flex-shrink:0}.detail-resize-handle{flex-shrink:0;background:rgba(0,0,0,0);position:relative;z-index:10}.detail-resize-handle:hover,.detail-resize-handle:active{background:var(--accent);opacity:.3}.detail-side .detail-resize-handle{width:4px;cursor:col-resize}.detail-bottom .detail-resize-handle{height:4px;cursor:row-resize}.detail-panel{border-left:1px solid var(--border);background:var(--bg);flex-shrink:0;display:flex;flex-direction:column;overflow:hidden}.detail-side .detail-panel{width:360px;border-left:1px solid var(--border);border-top:none}.detail-bottom .detail-panel{height:300px;width:auto;border-left:none;border-top:1px solid var(--border)}.detail-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600}.detail-ticket-number{font-family:"SF Mono",Monaco,"Cascadia Code",monospace;font-size:13px;color:var(--accent)}.detail-close{font-size:20px;color:var(--text-muted);padding:0 4px}.detail-close:hover{color:var(--text)}.detail-body{flex:1;overflow-y:auto;padding:16px}.detail-fields-row{display:contents}.detail-bottom .detail-body{display:flex;flex-wrap:wrap;gap:0 16px;align-content:flex-start}.detail-bottom .detail-body .detail-fields-row{display:flex;gap:16px;width:100%}.detail-bottom .detail-body .detail-fields-row .detail-field{flex:1;min-width:0}.detail-bottom .detail-body .detail-field-full{width:100%;flex-basis:100%}.detail-field{margin-bottom:16px}.detail-field label{display:block;font-size:12px;font-weight:500;color:var(--text-secondary);margin-bottom:4px}.detail-field input[type=text],.detail-field textarea,.detail-field select{width:100%;padding:6px 10px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);outline:none}.detail-field input[type=text]:focus,.detail-field textarea:focus,.detail-field select:focus{border-color:var(--accent)}.detail-field textarea{resize:vertical;min-height:80px}.detail-upnext-label{display:flex !important;align-items:center;gap:8px;cursor:pointer;font-size:14px !important;font-weight:400 !important;color:var(--text) !important}.detail-upnext-label input[type=checkbox]{width:auto;accent-color:var(--star)}.detail-attachments{margin-bottom:8px}.attachment-item{display:flex;align-items:center;justify-content:space-between;padding:4px 8px;background:var(--bg-secondary);border-radius:var(--radius);margin-bottom:4px;font-size:12px}.attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.attachment-delete{color:var(--text-muted);font-size:16px;padding:0 4px;cursor:pointer;border:none;background:none}.attachment-delete:hover{color:var(--danger)}.upload-btn{cursor:pointer;display:inline-flex;align-items:center;gap:4px;margin-top:4px}.detail-meta{padding-top:12px;border-top:1px solid var(--border);font-size:11px;color:var(--text-muted)}.detail-meta div{margin-bottom:2px}.dropdown-menu{position:fixed;z-index:1000;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);padding:4px 0;min-width:140px}.dropdown-item{display:flex;align-items:center;gap:8px;width:100%;padding:6px 12px;text-align:left;font-size:13px;text-transform:capitalize}.dropdown-item:hover{background:var(--bg-hover)}.dropdown-item.active{color:var(--accent);font-weight:500}.dropdown-label{flex:1}.dropdown-kbd{font-size:11px;color:var(--text-muted);padding:1px 5px;border:1px solid var(--border);border-radius:3px;background:var(--bg-secondary);font-family:inherit;flex-shrink:0}.dropdown-dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}.settings-overlay{position:fixed;inset:0;background:rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;z-index:2000}.settings-dialog{background:var(--bg);border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,.15);width:380px;max-width:90vw}.settings-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border)}.settings-header h2{font-size:16px;font-weight:600}.settings-body{padding:20px}.settings-field{margin-bottom:16px}.settings-field:last-child{margin-bottom:0}.settings-field label{display:block;font-size:13px;font-weight:500;color:var(--text-secondary);margin-bottom:6px}.settings-field select,.settings-field input[type=number]{width:100%;padding:6px 10px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);outline:none}.settings-field select:focus,.settings-field input[type=number]:focus{border-color:var(--accent)}.app-footer{display:flex;align-items:center;justify-content:space-between;padding:6px 20px;border-top:1px solid var(--border);background:var(--bg-secondary);font-size:12px;color:var(--text-muted);flex-shrink:0}.keyboard-hints{display:flex;gap:16px}.keyboard-hints kbd{display:inline-block;padding:1px 5px;border:1px solid var(--border);border-radius:3px;background:var(--bg);font-family:inherit;font-size:11px}.status-bar{font-size:12px}@media(max-width: 768px){.sidebar{width:140px}.detail-side .detail-panel{width:280px}.keyboard-hints{display:none}}@media(max-width: 600px){.sidebar{display:none}}.error-popup{position:fixed;inset:0;background:rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;z-index:3000}.error-popup-content{background:var(--bg);border:1px solid var(--danger);border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,.15);padding:24px;max-width:360px;text-align:center}.error-popup-content strong{display:block;font-size:16px;margin-bottom:8px;color:var(--danger)}.error-popup-content p{font-size:14px;color:var(--text-secondary);margin-bottom:16px}.error-popup-content button{padding:6px 16px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);font-size:13px;cursor:pointer}.error-popup-content button:hover{background:var(--bg-hover)}.detail-notes{display:flex;flex-direction:column;gap:8px}.note-entry{padding:8px 10px;background:var(--bg-secondary);border-radius:var(--radius);border-left:3px solid var(--accent)}.note-timestamp{font-size:11px;color:var(--text-muted);margin-bottom:2px}.note-text{font-size:13px;color:var(--text);white-space:pre-wrap}
|
|
1
|
+
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}:root{--bg: #ffffff;--bg-secondary: #f9fafb;--bg-hover: #f3f4f6;--bg-selected: #eff6ff;--border: #e5e7eb;--text: #111827;--text-secondary: #6b7280;--text-muted: #9ca3af;--accent: #3b82f6;--accent-hover: #2563eb;--danger: #ef4444;--star: #eab308;--radius: 6px;--shadow: 0 1px 3px rgba(0,0,0,0.08)}html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-size:14px;line-height:1.5;color:var(--text);background:var(--bg)}button{cursor:pointer;border:none;background:none;font:inherit;color:inherit}input,select,textarea{font:inherit;color:inherit}.app{display:flex;flex-direction:column;height:100vh}.app-header{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-bottom:1px solid var(--border);background:var(--bg);gap:16px;flex-shrink:0}.app-header h1{font-size:18px;font-weight:700;white-space:nowrap}.header-controls{display:flex;align-items:center;gap:12px;flex:1;max-width:500px}.search-box{flex:1}.search-box input{width:100%;padding:6px 12px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg-secondary);outline:none}.search-box input:focus{border-color:var(--accent);background:var(--bg)}.layout-toggle{display:flex;border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;flex-shrink:0}.layout-btn{display:flex;align-items:center;justify-content:center;padding:4px 8px;color:var(--text-secondary);background:var(--bg);border:none;border-right:1px solid var(--border);cursor:pointer}.layout-btn:last-child{border-right:none}.layout-btn:hover:not(:disabled){background:var(--bg-hover);color:var(--text)}.layout-btn.active{background:var(--bg-selected);color:var(--accent)}.layout-btn:disabled{cursor:default}.sort-controls select{padding:6px 8px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);cursor:pointer}.settings-btn{font-size:18px;color:var(--text-muted);padding:4px;border-radius:var(--radius);flex-shrink:0}.settings-btn:hover{color:var(--text);background:var(--bg-hover)}.app-body{display:flex;flex:1;overflow:hidden}.content-area{flex:1;display:flex;overflow:hidden;min-width:0}.content-area.detail-side{flex-direction:row}.content-area.detail-bottom{flex-direction:column}.sidebar{width:180px;border-right:1px solid var(--border);background:var(--bg-secondary);overflow-y:auto;flex-shrink:0;padding:8px 0}.sidebar-copy-prompt{padding:8px 10px 4px}.copy-prompt-btn{display:flex;align-items:center;gap:6px;width:100%;padding:6px 8px;font-size:12px;color:var(--text-secondary);background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;transition:all .15s}.copy-prompt-btn:hover{color:var(--accent);border-color:var(--accent);background:var(--bg-selected)}.copy-prompt-icon{display:flex;align-items:center;flex-shrink:0}.sidebar-section{margin-bottom:8px}.sidebar-label{padding:8px 16px 4px;font-size:11px;font-weight:600;text-transform:uppercase;color:var(--text-muted);letter-spacing:.5px}.sidebar-item{display:flex;align-items:center;gap:8px;width:100%;padding:6px 16px;text-align:left;font-size:13px;color:var(--text-secondary);border-radius:0}.sidebar-item:hover{background:var(--bg-hover);color:var(--text)}.sidebar-item.active{background:var(--bg-selected);color:var(--accent);font-weight:500}.sidebar-item.drop-target{background:var(--bg-selected);color:var(--accent);outline:2px dashed var(--accent);outline-offset:-2px}.cat-dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}.sidebar-stats{padding:12px 16px;font-size:12px;color:var(--text-muted);border-top:1px solid var(--border);margin-top:8px}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0;min-height:0}.batch-toolbar{display:flex;align-items:center;gap:8px;padding:6px 16px 6px 19px;background:var(--bg-secondary);border-bottom:1px solid var(--border);flex-shrink:0;flex-wrap:wrap;min-height:36px}.batch-toolbar select:disabled,.batch-toolbar .btn:disabled{opacity:.4;cursor:default;pointer-events:none}.batch-toolbar select{padding:4px 8px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);font-size:12px}.batch-select-all{width:14px;height:14px;cursor:pointer;accent-color:var(--accent);flex-shrink:0}.batch-star-btn{font-size:16px;color:var(--text-muted);padding:2px 6px;border-radius:var(--radius);line-height:1}.batch-star-btn:hover:not(:disabled){color:var(--star);background:var(--bg-hover)}.batch-star-btn.active{color:var(--star)}.batch-star-btn.mixed{color:var(--star)}.batch-star-btn:disabled{opacity:.4;cursor:default;pointer-events:none}.star-mixed-wrap{position:relative;display:inline-block}.star-mixed-fill{position:absolute;left:0;top:0;overflow:hidden;width:50%}.batch-delete-btn.btn{display:inline-flex;align-items:center;justify-content:center;padding:4px 6px;border:none;background:none}.batch-count{font-size:12px;color:var(--text-muted);margin-left:auto;white-space:nowrap}.btn{padding:4px 10px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);font-size:12px;cursor:pointer}.btn:hover{background:var(--bg-hover)}.btn-sm{padding:3px 8px;font-size:11px}.btn-danger{color:var(--danger)}.btn-danger:hover:not(:disabled){background:#fef2f2}.ticket-list{flex:1;overflow-y:auto;padding:4px 0}.ticket-list-empty,.ticket-list-loading{padding:40px 20px;text-align:center;color:var(--text-muted);font-size:14px}.ticket-row{display:flex;align-items:center;gap:8px;padding:4px 16px;border-bottom:1px solid rgba(0,0,0,0);border-left:3px solid rgba(0,0,0,0);min-height:36px}.ticket-row:hover{background:var(--bg-hover)}.ticket-row.selected{background:var(--bg-selected)}.ticket-row.completed .ticket-title-input{text-decoration:line-through;color:var(--text-muted)}.ticket-row.up-next{border-left:3px solid var(--star)}.ticket-checkbox{flex-shrink:0;width:14px;height:14px;cursor:pointer;accent-color:var(--accent)}.ticket-status-btn{flex-shrink:0;width:20px;height:20px;font-size:14px;display:flex;align-items:center;justify-content:center;border-radius:50%;color:var(--text-secondary)}.ticket-status-btn:hover{background:var(--bg-hover)}.ticket-status-btn.verified{color:#22c55e}.ticket-category-badge{flex-shrink:0;width:4em;padding:1px 0;border-radius:3px;font-size:10px;font-weight:600;color:#fff;text-transform:uppercase;text-align:center;cursor:pointer;letter-spacing:.3px}.ticket-category-badge:hover{opacity:.85}.ticket-number{flex-shrink:0;font-size:11px;color:var(--text-muted);font-family:"SF Mono",Monaco,"Cascadia Code",monospace;min-width:5em}.ticket-title-input{flex:1;border:none;background:rgba(0,0,0,0);padding:2px 0;outline:none;min-width:0}.ticket-title-input:focus{border-bottom:1px solid var(--accent)}.ticket-title-input::placeholder{color:var(--text-muted)}.ticket-priority-indicator{flex-shrink:0;font-size:12px;cursor:pointer;padding:2px 4px;border-radius:3px;min-width:24px;text-align:center}.ticket-priority-indicator:hover{background:var(--bg-hover)}.ticket-star{flex-shrink:0;font-size:16px;color:var(--text-muted);padding:0}.ticket-star.active{color:var(--star)}.ticket-star:hover{color:var(--star)}.trash-row .trash-title{flex:1;color:var(--text-muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.trash-row .btn{flex-shrink:0}.draft-row{border-bottom:1px dashed var(--border) !important;background:var(--bg-secondary)}.draft-row:hover{background:var(--bg-secondary)}.draft-row .draft-placeholder{opacity:.35;pointer-events:none}.draft-row .draft-badge{opacity:.35}.draft-row .draft-input::placeholder{color:var(--text-muted);font-style:italic}.ticket-checkbox-spacer{width:14px;flex-shrink:0}.detail-resize-handle{flex-shrink:0;background:rgba(0,0,0,0);position:relative;z-index:10}.detail-resize-handle:hover,.detail-resize-handle:active{background:var(--accent);opacity:.3}.detail-side .detail-resize-handle{width:4px;cursor:col-resize}.detail-bottom .detail-resize-handle{height:4px;cursor:row-resize}.detail-panel{border-left:1px solid var(--border);background:var(--bg);flex-shrink:0;display:flex;flex-direction:column;overflow:hidden}.detail-side .detail-panel{width:360px;border-left:1px solid var(--border);border-top:none}.detail-bottom .detail-panel{height:300px;width:auto;border-left:none;border-top:1px solid var(--border)}.detail-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border);font-weight:600}.detail-ticket-number{font-family:"SF Mono",Monaco,"Cascadia Code",monospace;font-size:13px;color:var(--accent)}.detail-close{font-size:20px;color:var(--text-muted);padding:0 4px}.detail-close:hover{color:var(--text)}.detail-body{flex:1;overflow-y:auto;padding:16px}.detail-fields-row{display:contents}.detail-bottom .detail-body{display:flex;flex-wrap:wrap;gap:0 16px;align-content:flex-start}.detail-bottom .detail-body .detail-fields-row{display:flex;gap:16px;width:100%}.detail-bottom .detail-body .detail-fields-row .detail-field{flex:1;min-width:0}.detail-bottom .detail-body .detail-field-full{width:100%;flex-basis:100%}.detail-field{margin-bottom:16px}.detail-field label{display:block;font-size:12px;font-weight:500;color:var(--text-secondary);margin-bottom:4px}.detail-field input[type=text],.detail-field textarea,.detail-field select{width:100%;padding:6px 10px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);outline:none}.detail-field input[type=text]:focus,.detail-field textarea:focus,.detail-field select:focus{border-color:var(--accent)}.detail-field textarea{resize:vertical;min-height:80px}.detail-upnext-label{display:flex !important;align-items:center;gap:8px;cursor:pointer;font-size:14px !important;font-weight:400 !important;color:var(--text) !important}.detail-upnext-label input[type=checkbox]{width:auto;accent-color:var(--star)}.detail-attachments{margin-bottom:8px}.attachment-item{display:flex;align-items:center;justify-content:space-between;padding:4px 8px;background:var(--bg-secondary);border-radius:var(--radius);margin-bottom:4px;font-size:12px}.attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.attachment-delete{color:var(--text-muted);font-size:16px;padding:0 4px;cursor:pointer;border:none;background:none}.attachment-delete:hover{color:var(--danger)}.upload-btn{cursor:pointer;display:inline-flex;align-items:center;gap:4px;margin-top:4px}.detail-meta{padding-top:12px;border-top:1px solid var(--border);font-size:11px;color:var(--text-muted)}.detail-meta div{margin-bottom:2px}.dropdown-menu{position:fixed;z-index:1000;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);padding:4px 0;min-width:140px}.dropdown-item{display:flex;align-items:center;gap:8px;width:100%;padding:6px 12px;text-align:left;font-size:13px;text-transform:capitalize}.dropdown-item:hover{background:var(--bg-hover)}.dropdown-item.active{color:var(--accent);font-weight:500}.dropdown-label{flex:1}.dropdown-kbd{font-size:11px;color:var(--text-muted);padding:1px 5px;border:1px solid var(--border);border-radius:3px;background:var(--bg-secondary);font-family:inherit;flex-shrink:0}.dropdown-dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex-shrink:0}.settings-overlay{position:fixed;inset:0;background:rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;z-index:2000}.settings-dialog{background:var(--bg);border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,.15);width:380px;max-width:90vw}.settings-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border)}.settings-header h2{font-size:16px;font-weight:600}.settings-body{padding:20px}.settings-field{margin-bottom:16px}.settings-field:last-child{margin-bottom:0}.settings-field label{display:block;font-size:13px;font-weight:500;color:var(--text-secondary);margin-bottom:6px}.settings-field select,.settings-field input[type=number]{width:100%;padding:6px 10px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);outline:none}.settings-field select:focus,.settings-field input[type=number]:focus{border-color:var(--accent)}.app-footer{display:flex;align-items:center;justify-content:space-between;padding:6px 20px;border-top:1px solid var(--border);background:var(--bg-secondary);font-size:12px;color:var(--text-muted);flex-shrink:0}.keyboard-hints{display:flex;gap:16px}.keyboard-hints kbd{display:inline-block;padding:1px 5px;border:1px solid var(--border);border-radius:3px;background:var(--bg);font-family:inherit;font-size:11px}.status-bar{font-size:12px}.ticket-list-columns{display:flex;flex-direction:column;overflow:hidden}.ticket-list-columns .draft-row{flex-shrink:0}.columns-container{display:flex;flex:1;gap:1px;background:var(--border);overflow-x:auto;min-height:0}.column{flex:1;min-width:180px;display:flex;flex-direction:column;background:var(--bg-secondary);overflow:hidden}.column.column-drop-target{background:var(--bg-selected)}.column.column-drop-target .column-body{outline:2px dashed var(--accent);outline-offset:-2px}.column-header{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;font-size:12px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.3px;border-bottom:1px solid var(--border);background:var(--bg);flex-shrink:0}.column-count{font-size:11px;font-weight:500;color:var(--text-muted);background:var(--bg-secondary);padding:1px 6px;border-radius:10px}.column-body{flex:1;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:6px}.column-card{padding:8px 10px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);cursor:grab;box-shadow:var(--shadow)}.column-card:hover{border-color:var(--accent)}.column-card.selected{border-color:var(--accent);background:var(--bg-selected)}.column-card.up-next{border-left:3px solid var(--star)}.column-card:active{cursor:grabbing}.column-card-header{display:flex;align-items:center;gap:6px;margin-bottom:4px}.column-card-title{font-size:13px;line-height:1.4;word-break:break-word}@media(max-width: 768px){.sidebar{width:140px}.detail-side .detail-panel{width:280px}.keyboard-hints{display:none}}@media(max-width: 600px){.sidebar{display:none}}.error-popup{position:fixed;inset:0;background:rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;z-index:3000}.error-popup-content{background:var(--bg);border:1px solid var(--danger);border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,.15);padding:24px;max-width:360px;text-align:center}.error-popup-content strong{display:block;font-size:16px;margin-bottom:8px;color:var(--danger)}.error-popup-content p{font-size:14px;color:var(--text-secondary);margin-bottom:16px}.error-popup-content button{padding:6px 16px;border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);font-size:13px;cursor:pointer}.error-popup-content button:hover{background:var(--bg-hover)}.detail-notes{display:flex;flex-direction:column;gap:8px}.note-entry{padding:8px 10px;background:var(--bg-secondary);border-radius:var(--radius);border-left:3px solid var(--accent)}.note-timestamp{font-size:11px;color:var(--text-muted);margin-bottom:2px}.note-text{font-size:13px;color:var(--text);white-space:pre-wrap}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hotsheet",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "A lightweight local project management tool. Create, categorize, and prioritize tickets with a fast bullet-list interface, then export an Up Next worklist for AI tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,8 +33,13 @@
|
|
|
33
33
|
"build:client": "mkdir -p dist/client && npx esbuild src/client/app.ts --bundle --format=iife --outfile=dist/client/app.global.js --target=es2020 --jsx=automatic --jsx-import-source=#jsx --alias:#jsx/jsx-runtime=./src/jsx-runtime.ts && npx sass src/client/styles.scss dist/client/styles.css --style compressed --no-source-map",
|
|
34
34
|
"lint": "eslint src/",
|
|
35
35
|
"clean": "rm -rf dist node_modules/.cache",
|
|
36
|
+
"tauri:clean": "rm -rf src-tauri/target",
|
|
36
37
|
"release": "bash scripts/release.sh",
|
|
37
|
-
"prepublishOnly": "npm run build"
|
|
38
|
+
"prepublishOnly": "npm run build",
|
|
39
|
+
"dev:server": "npm run build:client && tsx --tsconfig tsconfig.json src/cli.ts --no-open --strict-port",
|
|
40
|
+
"tauri": "tauri",
|
|
41
|
+
"tauri:dev": "bash scripts/ensure-sidecar-stub.sh && tauri dev",
|
|
42
|
+
"tauri:build": "bash scripts/build-sidecar.sh && tauri build"
|
|
38
43
|
},
|
|
39
44
|
"files": [
|
|
40
45
|
"dist",
|
|
@@ -57,6 +62,7 @@
|
|
|
57
62
|
"tsup": "^8.5.1",
|
|
58
63
|
"tsx": "^4.21.0",
|
|
59
64
|
"typescript": "^5.9.3",
|
|
65
|
+
"@tauri-apps/cli": "^2",
|
|
60
66
|
"typescript-eslint": "^8.56.1"
|
|
61
67
|
}
|
|
62
68
|
}
|