hotsheet 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -63,13 +63,19 @@ The loop stays tight because the AI always knows what to work on next.
63
63
  <img src="docs/demo-3.png" alt="Sidebar filtering by Bug category" width="900">
64
64
  </p>
65
65
 
66
- **Batch operations** — select multiple tickets to bulk-update category, priority, status, or Up Next.
66
+ **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.
67
+
68
+ <p align="center">
69
+ <img src="docs/demo-7.png" alt="Column view showing tickets organized by status in a kanban board" width="900">
70
+ </p>
71
+
72
+ **Batch operations** — select multiple tickets to bulk-update category, priority, status, or Up Next. Multi-select works in both list and column views.
67
73
 
68
74
  <p align="center">
69
75
  <img src="docs/demo-5.png" alt="Multiple tickets selected with the batch toolbar visible" width="900">
70
76
  </p>
71
77
 
72
- **Detail panel** — side or bottom orientation, resizable, with fields for title, details, attachments, and timestamped notes. Auto-shows when you select a ticket.
78
+ **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
79
 
74
80
  <p align="center">
75
81
  <img src="docs/demo-6.png" alt="Detail panel in bottom orientation showing ticket details and notes" width="900">
@@ -78,12 +84,13 @@ The loop stays tight because the AI always knows what to work on next.
78
84
  **Also includes:**
79
85
  - **Five priority levels** — Highest to Lowest, sortable and filterable
80
86
  - **Up Next flag** — star tickets to add them to the AI worklist
87
+ - **Drag and drop** — drag tickets onto sidebar views to change category, priority, or status
81
88
  - **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
89
+ - **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
90
+ - **Copy for commits** — `Cmd+C` copies selected ticket info (number + title) for use in commit messages
83
91
  - **File attachments** — attach files to any ticket
84
92
  - **Markdown sync** — `worklist.md` and `open-tickets.md` auto-generated on every change
85
93
  - **Auto-cleanup** — configurable auto-deletion of old trash and verified items
86
- - **Settings** — detail panel position, cleanup intervals, all persisted
87
94
  - **Fully local** — embedded PostgreSQL (PGLite), no network calls, no accounts, no telemetry
88
95
 
89
96
  ---
@@ -189,6 +196,7 @@ hotsheet --data-dir ~/projects/my-app/.hotsheet
189
196
  | `Cmd+G` | Set category: Investigation |
190
197
  | `Alt+1-5` | Set priority (Highest to Lowest) |
191
198
  | `Cmd+D` | Toggle Up Next |
199
+ | `Cmd+C` | Copy ticket info (number + title) |
192
200
  | `Cmd+A` | Select all |
193
201
  | `Escape` | Clear selection / close |
194
202
 
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
@@ -1150,12 +1272,14 @@ var CATEGORY_DESCRIPTIONS = {
1150
1272
 
1151
1273
  // src/sync/markdown.ts
1152
1274
  var dataDir;
1275
+ var port;
1153
1276
  var worklistTimeout = null;
1154
1277
  var openTicketsTimeout = null;
1155
1278
  var WORKLIST_DEBOUNCE = 500;
1156
1279
  var OPEN_TICKETS_DEBOUNCE = 5e3;
1157
- function initMarkdownSync(dir) {
1280
+ function initMarkdownSync(dir, serverPort) {
1158
1281
  dataDir = dir;
1282
+ port = serverPort;
1159
1283
  }
1160
1284
  function scheduleWorklistSync() {
1161
1285
  if (worklistTimeout) clearTimeout(worklistTimeout);
@@ -1233,15 +1357,19 @@ async function syncWorklist() {
1233
1357
  sections.push("");
1234
1358
  sections.push("## Workflow");
1235
1359
  sections.push("");
1236
- sections.push("The Hot Sheet API is available at http://localhost:4174/api. Use it to update ticket status as you work:");
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.`);
1237
1361
  sections.push("");
1238
- sections.push('- **When you start working on a ticket**, set its status to "started":');
1239
- sections.push(' `curl -X PATCH http://localhost:4174/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"}'\``);
1240
1364
  sections.push("");
1241
- sections.push('- **When you finish working on a ticket**, set its status to "completed" and add notes describing what was done:');
1242
- sections.push(' `curl -X PATCH http://localhost:4174/api/tickets/{id} -H "Content-Type: application/json" -d \'{"status": "completed", "notes": "Description of work completed"}\'`');
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"}'\``);
1243
1367
  sections.push("");
1244
- sections.push('Do NOT set tickets to "verified" \u2014 that status is reserved for human review.');
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.');
1245
1373
  sections.push("");
1246
1374
  if (tickets.length === 0) {
1247
1375
  sections.push("No items in the Up Next list.");
@@ -1679,6 +1807,10 @@ pageRoutes.get("/", (c) => {
1679
1807
  /* @__PURE__ */ jsx("div", { className: "app-title", children: /* @__PURE__ */ jsx("h1", { children: "Hot Sheet" }) }),
1680
1808
  /* @__PURE__ */ jsx("div", { className: "header-controls", children: [
1681
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
+ ] }),
1682
1814
  /* @__PURE__ */ jsx("div", { className: "sort-controls", children: /* @__PURE__ */ jsx("select", { id: "sort-select", children: [
1683
1815
  /* @__PURE__ */ jsx("option", { value: "created:desc", children: "Newest First" }),
1684
1816
  /* @__PURE__ */ jsx("option", { value: "created:asc", children: "Oldest First" }),
@@ -1686,6 +1818,10 @@ pageRoutes.get("/", (c) => {
1686
1818
  /* @__PURE__ */ jsx("option", { value: "category:asc", children: "Category" }),
1687
1819
  /* @__PURE__ */ jsx("option", { value: "status:asc", children: "Status" })
1688
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
+ ] }),
1689
1825
  /* @__PURE__ */ jsx("button", { className: "settings-btn", id: "settings-btn", title: "Settings", children: raw("&#9881;") })
1690
1826
  ] })
1691
1827
  ] }),
@@ -1698,6 +1834,7 @@ pageRoutes.get("/", (c) => {
1698
1834
  /* @__PURE__ */ jsx("div", { className: "sidebar-section", children: [
1699
1835
  /* @__PURE__ */ jsx("div", { className: "sidebar-label", children: "Views" }),
1700
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" }),
1701
1838
  /* @__PURE__ */ jsx("button", { className: "sidebar-item", "data-view": "up-next", children: "Up Next" }),
1702
1839
  /* @__PURE__ */ jsx("button", { className: "sidebar-item", "data-view": "open", children: "Open" }),
1703
1840
  /* @__PURE__ */ jsx("button", { className: "sidebar-item", "data-view": "completed", children: "Completed" }),
@@ -1881,13 +2018,6 @@ pageRoutes.get("/", (c) => {
1881
2018
  /* @__PURE__ */ jsx("button", { className: "detail-close", id: "settings-close", children: raw("&times;") })
1882
2019
  ] }),
1883
2020
  /* @__PURE__ */ jsx("div", { className: "settings-body", children: [
1884
- /* @__PURE__ */ jsx("div", { className: "settings-field", children: [
1885
- /* @__PURE__ */ jsx("label", { children: "Detail Panel Position" }),
1886
- /* @__PURE__ */ jsx("select", { id: "settings-detail-position", children: [
1887
- /* @__PURE__ */ jsx("option", { value: "side", children: "Side" }),
1888
- /* @__PURE__ */ jsx("option", { value: "bottom", children: "Bottom" })
1889
- ] })
1890
- ] }),
1891
2021
  /* @__PURE__ */ jsx("div", { className: "settings-field", children: [
1892
2022
  /* @__PURE__ */ jsx("label", { children: "Auto-clear trash after (days)" }),
1893
2023
  /* @__PURE__ */ jsx("input", { type: "number", id: "settings-trash-days", min: "1", value: "3" })
@@ -1903,18 +2033,18 @@ pageRoutes.get("/", (c) => {
1903
2033
  });
1904
2034
 
1905
2035
  // src/server.ts
1906
- function tryServe(fetch, port) {
2036
+ function tryServe(fetch, port2) {
1907
2037
  return new Promise((resolve2, reject) => {
1908
- const server = serve({ fetch, port });
2038
+ const server = serve({ fetch, port: port2 });
1909
2039
  server.on("listening", () => {
1910
- resolve2(port);
2040
+ resolve2(port2);
1911
2041
  });
1912
2042
  server.on("error", (err) => {
1913
2043
  reject(err);
1914
2044
  });
1915
2045
  });
1916
2046
  }
1917
- async function startServer(port, dataDir2) {
2047
+ async function startServer(port2, dataDir2) {
1918
2048
  const app = new Hono3();
1919
2049
  app.use("*", async (c, next) => {
1920
2050
  c.set("dataDir", dataDir2);
@@ -1932,10 +2062,10 @@ async function startServer(port, dataDir2) {
1932
2062
  });
1933
2063
  app.route("/api", apiRoutes);
1934
2064
  app.route("/", pageRoutes);
1935
- let actualPort = port;
2065
+ let actualPort = port2;
1936
2066
  for (let attempt = 0; attempt < 20; attempt++) {
1937
2067
  try {
1938
- actualPort = await tryServe(app.fetch, port + attempt);
2068
+ actualPort = await tryServe(app.fetch, port2 + attempt);
1939
2069
  break;
1940
2070
  } catch (err) {
1941
2071
  if (err instanceof Error && err.code === "EADDRINUSE" && attempt < 19) {
@@ -1944,8 +2074,8 @@ async function startServer(port, dataDir2) {
1944
2074
  throw err;
1945
2075
  }
1946
2076
  }
1947
- if (actualPort !== port) {
1948
- console.log(` Port ${port} in use, using ${actualPort} instead.`);
2077
+ if (actualPort !== port2) {
2078
+ console.log(` Port ${port2} in use, using ${actualPort} instead.`);
1949
2079
  }
1950
2080
  const url = `http://localhost:${actualPort}`;
1951
2081
  console.log(`
@@ -1953,6 +2083,7 @@ async function startServer(port, dataDir2) {
1953
2083
  `);
1954
2084
  const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1955
2085
  exec(`${openCmd} ${url}`);
2086
+ return actualPort;
1956
2087
  }
1957
2088
 
1958
2089
  // src/update-check.ts
@@ -2088,7 +2219,7 @@ Examples:
2088
2219
  }
2089
2220
  function parseArgs(argv) {
2090
2221
  const args = argv.slice(2);
2091
- let port = 4174;
2222
+ let port2 = 4174;
2092
2223
  let dataDir2 = join7(process.cwd(), ".hotsheet");
2093
2224
  let demo = null;
2094
2225
  let forceUpdateCheck = false;
@@ -2109,8 +2240,8 @@ function parseArgs(argv) {
2109
2240
  process.exit(0);
2110
2241
  break;
2111
2242
  case "--port":
2112
- port = parseInt(args[++i], 10);
2113
- if (isNaN(port)) {
2243
+ port2 = parseInt(args[++i], 10);
2244
+ if (isNaN(port2)) {
2114
2245
  console.error("Invalid port number");
2115
2246
  process.exit(1);
2116
2247
  }
@@ -2127,7 +2258,7 @@ function parseArgs(argv) {
2127
2258
  process.exit(1);
2128
2259
  }
2129
2260
  }
2130
- return { port, dataDir: dataDir2, demo, forceUpdateCheck };
2261
+ return { port: port2, dataDir: dataDir2, demo, forceUpdateCheck };
2131
2262
  }
2132
2263
  async function main() {
2133
2264
  const parsed = parseArgs(process.argv);
@@ -2135,7 +2266,7 @@ async function main() {
2135
2266
  printUsage();
2136
2267
  process.exit(1);
2137
2268
  }
2138
- const { port, demo, forceUpdateCheck } = parsed;
2269
+ const { port: port2, demo, forceUpdateCheck } = parsed;
2139
2270
  let { dataDir: dataDir2 } = parsed;
2140
2271
  await checkForUpdates(forceUpdateCheck);
2141
2272
  if (demo !== null) {
@@ -2162,13 +2293,13 @@ async function main() {
2162
2293
  if (demo !== null) {
2163
2294
  await seedDemoData(demo);
2164
2295
  }
2165
- initMarkdownSync(dataDir2);
2166
- scheduleAllSync();
2167
2296
  if (demo === null) {
2168
2297
  await cleanupAttachments();
2169
2298
  }
2170
2299
  console.log(` Data directory: ${dataDir2}`);
2171
- await startServer(port, dataDir2);
2300
+ const actualPort = await startServer(port2, dataDir2);
2301
+ initMarkdownSync(dataDir2, actualPort);
2302
+ scheduleAllSync();
2172
2303
  }
2173
2304
  main().catch((err) => {
2174
2305
  console.error(err);
@@ -1 +1,4 @@
1
- "use strict";(()=>{var re=Object.defineProperty;var le=(e,t,i)=>t in e?re(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i;var z=(e,t,i)=>le(e,typeof t!="symbol"?t+"":t,i);function F(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function j(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}var h=class{constructor(t){z(this,"__html");this.__html=t}toString(){return this.__html}};function b(e){return new h(e)}var de=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 ce(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=j(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])=>ce(d,r)).join("");if(de.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 f(e){let t=document.createElement("template");return t.innerHTML=e.toString(),t.content.firstElementChild}function V(e){document.getElementById("network-error-popup")?.remove();let t=f(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:b("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 V("Unable to reach the server. It may have been stopped."),i}}async function W(e,t){try{let i=new FormData;return i.append("file",t),(await fetch("/api"+e,{method:"POST",body:i})).json()}catch(i){throw V("Unable to reach the server. It may have been stopped."),i}}var ue={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:{...ue}},me={issue:"#6b7280",bug:"#ef4444",feature:"#22c55e",requirement_change:"#f97316",task:"#3b82f6",investigation:"#8b5cf6"},pe={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"},fe={highest:"#ef4444",high:"#f97316",default:"#6b7280",low:"#3b82f6",lowest:"#94a3b8"},ge={not_started:"\u25CB",started:"\u25D4",completed:"\u2713",verified:"svg"},G='<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 k(e){return me[e]||"#6b7280"}function S(e){return pe[e]||"ISS"}function Y(e){return ye[e]||"\u2014"}function A(e){return fe[e]||"#6b7280"}function J(e){return ge[e]||"\u25CB"}function x(e){n.activeTicketId=e,Z(e)}function X(){n.selectedIds.clear(),n.activeTicketId=null;let e=new CustomEvent("hotsheet:render");document.dispatchEvent(e)}function Q(){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,Z(s))}else n.activeTicketId!=null&&(n.activeTicketId=null),t.style.display="none",i&&(i.style.display="none")}async function Z(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:b("&times;")})]}))}).toString():i.innerHTML="";let s=document.getElementById("detail-notes-section"),a=document.getElementById("detail-notes"),o=he(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 he(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 ee(){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 P(e){let t=document.getElementById("content-area");t.classList.remove("detail-side","detail-bottom"),t.classList.add(e==="bottom"?"detail-bottom":"detail-side")}function N(){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 te(){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=f(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(m=>r.key.toLowerCase()===m.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,I=!1,T=null,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"}],R=[{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 ve(){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&&(I=!0,e==="draft"?g():document.querySelector(`.ticket-row[data-id="${e}"] .ticket-title-input`)?.focus(),I=!1)}function y(){let e=n.view==="trash",t=ve(),i=document.getElementById("ticket-list");i.innerHTML="",e||i.appendChild(ke()),e&&n.tickets.length===0&&i.appendChild(f(l("div",{className:"ticket-list-empty",children:"Trash is empty"})));for(let s of n.tickets)i.appendChild(e?we(s):Ie(s));be(t),B(),ee()}function ke(){let e=se(),t=n.view.startsWith("category:"),i=f(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:${k(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..."}),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(),Ee(a)})}let s=i.querySelector(".draft-input");return s.addEventListener("keydown",async a=>{if(a.key==="Enter"&&s.value.trim()){a.preventDefault();let o=s.value.trim();s.value="";let d=Te();T&&!n.view.startsWith("category:")&&(d.category=T),await c("/tickets",{method:"POST",body:{title:o,defaults:d}}),T=null,await p(),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 Te(){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 se(){if(T)return T;let e=n.view;return e.startsWith("category:")?e.split(":")[1]:"issue"}function Ee(e){C();let i=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",s=se(),a=H(e,M.map(o=>({label:o.label,key:o.key,shortcut:`${i}${o.key.toUpperCase()}`,color:k(o.value),active:s===o.value,action:()=>{T=o.value,y(),g()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function Ie(e){let t=n.selectedIds.has(e.id),i=e.status==="completed"||e.status==="verified",s=e.status==="verified",a=f(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?b(G):J(e.status)}),l("span",{className:"ticket-category-badge",style:`background-color:${k(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:Y(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",m=>{(m.metaKey||m.ctrlKey||m.shiftKey)&&(m.preventDefault(),ae(m,e)&&m.stopPropagation())});let o=a.querySelector(".ticket-checkbox");o.addEventListener("click",m=>m.stopPropagation()),o.addEventListener("change",()=>{o.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,y()}),a.querySelector(".ticket-status-btn").addEventListener("click",m=>{m.stopPropagation(),_e(e)});let d=a.querySelector(".ticket-category-badge");d.addEventListener("click",m=>{m.stopPropagation(),Be(d,e)});let r=a.querySelector(".ticket-title-input");r.addEventListener("focus",()=>{I||n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,O(),B())}),r.addEventListener("input",()=>{Me(e.id,{title:r.value})}),r.addEventListener("keydown",m=>{Le(m,e,r)});let u=a.querySelector(".ticket-priority-indicator");return u.addEventListener("click",m=>{m.stopPropagation(),De(u,e)}),a.querySelector(".ticket-star").addEventListener("click",m=>{m.stopPropagation(),He(e)}),a}function we(e){let t=n.selectedIds.has(e.id),i=e.deleted_at?new Date(e.deleted_at):null,s=f(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:${k(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(),ae(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,y()}),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,O(),B())}),s.querySelector(".btn").addEventListener("click",async o=>{o.stopPropagation(),await c(`/tickets/${e.id}/restore`,{method:"POST"}),p()}),s}function ae(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,y();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 m=r;m<=u;m++)n.selectedIds.add(a[m])}y()}else return!1;return!0}function Le(e,t,i){if(e.key==="Enter")e.preventDefault(),g();else if(e.key==="Backspace"&&i.value==="")e.preventDefault(),Ce(t.id);else if(e.key==="ArrowDown"&&e.shiftKey)e.preventDefault(),ne(t.id,1);else if(e.key==="ArrowUp"&&e.shiftKey)e.preventDefault(),ne(t.id,-1);else if(e.key==="ArrowDown")e.preventDefault(),Se(t.id);else if(e.key==="ArrowUp")e.preventDefault(),xe(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);ie(t,"category",s.value)}else if(e.altKey&&!e.metaKey&&!e.ctrlKey&&R.some(s=>s.key===e.key)){e.preventDefault();let s=R.find(a=>a.key===e.key);ie(t,"priority",s.value)}}function Se(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 xe(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 ne(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),I=!0,document.querySelector(`.ticket-row[data-id="${a}"] .ticket-title-input`)?.focus(),I=!1,O(),B()}async function _e(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),y()}async function He(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),y();return}let t=await c(`/tickets/${e.id}/up-next`,{method:"POST"});Object.assign(e,t),y()}async function ie(e,t,i){let s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{[t]:i}});Object.assign(e,s),y()}async function Ce(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),y(),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 Me(e,t){$&&clearTimeout($),$=setTimeout(()=>{c(`/tickets/${e}`,{method:"PATCH",body:t})},300)}function Be(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:k(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),y()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function De(e,t){C();let i=H(e,R.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),y()}})));document.body.appendChild(i),_(i,e),i.style.visibility=""}function O(){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=f(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()}),v.insertBefore(d,document.getElementById("batch-count"))),d.disabled=!i,d.style.display="",r||(r=f(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(),p())}),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"),m=document.getElementById("batch-upnext");if(!s&&u&&i){let v=n.tickets.filter(E=>n.selectedIds.has(E.id)),w=v.every(E=>E.up_next),oe=v.every(E=>!E.up_next);w?(u.textContent="\u2605",m.classList.add("active"),m.classList.remove("mixed")):oe?(u.textContent="\u2606",m.classList.remove("active","mixed")):(u.innerHTML=l("span",{className:"star-mixed-wrap",children:[l("span",{className:"star-mixed-fill",children:"\u2605"}),"\u2606"]}).toString(),m.classList.remove("active"),m.classList.add("mixed"))}else u&&(u.textContent="\u2606",m.classList.remove("active","mixed"));Q()}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==="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:""}`),y()}async function Ae(){await Pe(),await p(),Re(),Oe(),qe(),Ke(),Ue(),ze(),Ne(),$e(),te(),Fe(),document.addEventListener("hotsheet:render",()=>y()),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{}P(n.settings.detail_position),N()}function Ne(){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,P(n.settings.detail_position),N(),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 $e(){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 Re(){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(),p()})})}function Oe(){let e=document.getElementById("sort-select");e.addEventListener("change",()=>{let[t,i]=e.value.split(":");n.sortBy=t,n.sortDir=i,p()})}var q=null;function qe(){let e=document.getElementById("search-input");e.addEventListener("input",()=>{q&&clearTimeout(q),q=setTimeout(()=>{n.search=e.value,p()},200)}),e.addEventListener("keydown",t=>{t.key==="Escape"&&(e.value="",n.search="",p())})}function Ke(){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 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="",p())}),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}}),p()}),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(),p())}),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();y()})}var K=null;function Ue(){document.getElementById("detail-close").addEventListener("click",X);let e=["detail-title","detail-details"];for(let i of e){let s=document.getElementById(i);s.addEventListener("input",()=>{K&&clearTimeout(K),K=setTimeout(()=>{if(n.activeTicketId==null)return;let a=i.replace("detail-","");c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:s.value}}).then(()=>{p()})},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}}),p()})}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"});p(),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 W(`/tickets/${n.activeTicketId}/attachments`,a),s.value="",x(n.activeTicketId),p())}),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 ze(){document.addEventListener("keydown",e=>{let t=e.target.tagName,i=t==="INPUT"||t==="TEXTAREA"||t==="SELECT",s=document.getElementById("settings-overlay");if(e.key==="Escape"&&s.style.display!=="none"){s.style.display="none";return}if(e.key==="Escape"){n.selectedIds.size>0&&(n.selectedIds.clear(),y());return}if((e.metaKey||e.ctrlKey)&&e.key==="a"&&!i){e.preventDefault(),n.selectedIds.clear();for(let a of n.tickets)n.selectedIds.add(a.id);y();return}if((e.metaKey||e.ctrlKey)&&e.key==="d"){if(n.selectedIds.size>0){e.preventDefault();let a=n.tickets.filter(r=>n.selectedIds.has(r.id)),d=!a.every(r=>r.up_next);if(d){let r=a.filter(u=>u.status==="completed"||u.status==="verified");if(r.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:r.map(u=>u.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==="n"){e.preventDefault(),g();return}if((e.metaKey||e.ctrlKey)&&e.key==="f"){e.preventDefault(),document.getElementById("search-input").focus();return}if(e.key==="n"&&!i){e.preventDefault(),g();return}})}var U=0;function Fe(){async function e(){try{let t=await c(`/poll?version=${U}`);t.version>U&&(U=t.version,p())}catch{await new Promise(t=>setTimeout(t,5e3))}setTimeout(e,100)}e()}Ae();})();
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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Y(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}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("&times;")})]}))}).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
+
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();})();
@@ -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.1",
3
+ "version": "0.2.0",
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",