hotsheet 0.1.0 → 0.1.2

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/dist/cli.js CHANGED
@@ -72,9 +72,9 @@ var init_gitignore = __esm({
72
72
  });
73
73
 
74
74
  // src/cli.ts
75
- import { mkdirSync as mkdirSync3 } from "fs";
75
+ import { mkdirSync as mkdirSync4 } from "fs";
76
76
  import { tmpdir } from "os";
77
- import { join as join6, resolve } from "path";
77
+ import { join as join7, resolve } from "path";
78
78
 
79
79
  // src/cleanup.ts
80
80
  import { rmSync as rmSync2 } from "fs";
@@ -1150,12 +1150,14 @@ var CATEGORY_DESCRIPTIONS = {
1150
1150
 
1151
1151
  // src/sync/markdown.ts
1152
1152
  var dataDir;
1153
+ var port;
1153
1154
  var worklistTimeout = null;
1154
1155
  var openTicketsTimeout = null;
1155
1156
  var WORKLIST_DEBOUNCE = 500;
1156
1157
  var OPEN_TICKETS_DEBOUNCE = 5e3;
1157
- function initMarkdownSync(dir) {
1158
+ function initMarkdownSync(dir, serverPort) {
1158
1159
  dataDir = dir;
1160
+ port = serverPort;
1159
1161
  }
1160
1162
  function scheduleWorklistSync() {
1161
1163
  if (worklistTimeout) clearTimeout(worklistTimeout);
@@ -1233,13 +1235,13 @@ async function syncWorklist() {
1233
1235
  sections.push("");
1234
1236
  sections.push("## Workflow");
1235
1237
  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:");
1238
+ sections.push(`The Hot Sheet API is available at http://localhost:${port}/api. Use it to update ticket status as you work:`);
1237
1239
  sections.push("");
1238
1240
  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"}\'`');
1241
+ sections.push(` \`curl -X PATCH http://localhost:${port}/api/tickets/{id} -H "Content-Type: application/json" -d '{"status": "started"}'\``);
1240
1242
  sections.push("");
1241
1243
  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"}\'`');
1244
+ sections.push(` \`curl -X PATCH http://localhost:${port}/api/tickets/{id} -H "Content-Type: application/json" -d '{"status": "completed", "notes": "Description of work completed"}'\``);
1243
1245
  sections.push("");
1244
1246
  sections.push('Do NOT set tickets to "verified" \u2014 that status is reserved for human review.');
1245
1247
  sections.push("");
@@ -1475,8 +1477,8 @@ apiRoutes.post("/tickets/:id/attachments", async (c) => {
1475
1477
  mkdirSync2(attachDir, { recursive: true });
1476
1478
  const storedPath = join4(attachDir, storedName);
1477
1479
  const buffer = Buffer.from(await file.arrayBuffer());
1478
- const { writeFileSync: writeFileSync3 } = await import("fs");
1479
- writeFileSync3(storedPath, buffer);
1480
+ const { writeFileSync: writeFileSync4 } = await import("fs");
1481
+ writeFileSync4(storedPath, buffer);
1480
1482
  const attachment = await addAttachment(id, originalName, storedPath);
1481
1483
  scheduleAllSync();
1482
1484
  notifyChange();
@@ -1501,8 +1503,8 @@ apiRoutes.get("/attachments/file/*", async (c) => {
1501
1503
  if (!existsSync2(fullPath)) {
1502
1504
  return c.json({ error: "File not found" }, 404);
1503
1505
  }
1504
- const { readFileSync: readFileSync3 } = await import("fs");
1505
- const content = readFileSync3(fullPath);
1506
+ const { readFileSync: readFileSync4 } = await import("fs");
1507
+ const content = readFileSync4(fullPath);
1506
1508
  const ext = extname(fullPath).toLowerCase();
1507
1509
  const mimeTypes = {
1508
1510
  ".png": "image/png",
@@ -1903,18 +1905,18 @@ pageRoutes.get("/", (c) => {
1903
1905
  });
1904
1906
 
1905
1907
  // src/server.ts
1906
- function tryServe(fetch, port) {
1908
+ function tryServe(fetch, port2) {
1907
1909
  return new Promise((resolve2, reject) => {
1908
- const server = serve({ fetch, port });
1910
+ const server = serve({ fetch, port: port2 });
1909
1911
  server.on("listening", () => {
1910
- resolve2(port);
1912
+ resolve2(port2);
1911
1913
  });
1912
1914
  server.on("error", (err) => {
1913
1915
  reject(err);
1914
1916
  });
1915
1917
  });
1916
1918
  }
1917
- async function startServer(port, dataDir2) {
1919
+ async function startServer(port2, dataDir2) {
1918
1920
  const app = new Hono3();
1919
1921
  app.use("*", async (c, next) => {
1920
1922
  c.set("dataDir", dataDir2);
@@ -1932,10 +1934,10 @@ async function startServer(port, dataDir2) {
1932
1934
  });
1933
1935
  app.route("/api", apiRoutes);
1934
1936
  app.route("/", pageRoutes);
1935
- let actualPort = port;
1937
+ let actualPort = port2;
1936
1938
  for (let attempt = 0; attempt < 20; attempt++) {
1937
1939
  try {
1938
- actualPort = await tryServe(app.fetch, port + attempt);
1940
+ actualPort = await tryServe(app.fetch, port2 + attempt);
1939
1941
  break;
1940
1942
  } catch (err) {
1941
1943
  if (err instanceof Error && err.code === "EADDRINUSE" && attempt < 19) {
@@ -1944,8 +1946,8 @@ async function startServer(port, dataDir2) {
1944
1946
  throw err;
1945
1947
  }
1946
1948
  }
1947
- if (actualPort !== port) {
1948
- console.log(` Port ${port} in use, using ${actualPort} instead.`);
1949
+ if (actualPort !== port2) {
1950
+ console.log(` Port ${port2} in use, using ${actualPort} instead.`);
1949
1951
  }
1950
1952
  const url = `http://localhost:${actualPort}`;
1951
1953
  console.log(`
@@ -1955,6 +1957,117 @@ async function startServer(port, dataDir2) {
1955
1957
  exec(`${openCmd} ${url}`);
1956
1958
  }
1957
1959
 
1960
+ // src/update-check.ts
1961
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1962
+ import { get } from "https";
1963
+ import { homedir } from "os";
1964
+ import { dirname as dirname2, join as join6 } from "path";
1965
+ import { fileURLToPath as fileURLToPath2 } from "url";
1966
+ var DATA_DIR = join6(homedir(), ".hotsheet");
1967
+ var CHECK_FILE = join6(DATA_DIR, "last-update-check");
1968
+ var PACKAGE_NAME = "hotsheet";
1969
+ function getCurrentVersion() {
1970
+ try {
1971
+ const dir = dirname2(fileURLToPath2(import.meta.url));
1972
+ const pkg = JSON.parse(readFileSync3(join6(dir, "..", "package.json"), "utf-8"));
1973
+ return pkg.version;
1974
+ } catch {
1975
+ return "0.0.0";
1976
+ }
1977
+ }
1978
+ function getLastCheckDate() {
1979
+ try {
1980
+ if (existsSync4(CHECK_FILE)) {
1981
+ return readFileSync3(CHECK_FILE, "utf-8").trim();
1982
+ }
1983
+ } catch {
1984
+ }
1985
+ return null;
1986
+ }
1987
+ function saveCheckDate() {
1988
+ mkdirSync3(DATA_DIR, { recursive: true });
1989
+ writeFileSync3(CHECK_FILE, (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), "utf-8");
1990
+ }
1991
+ function isFirstUseToday() {
1992
+ const last = getLastCheckDate();
1993
+ if (last === null) return true;
1994
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1995
+ return last !== today;
1996
+ }
1997
+ function fetchLatestVersion() {
1998
+ return new Promise((resolve2) => {
1999
+ const req = get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5e3 }, (res) => {
2000
+ if (res.statusCode !== 200) {
2001
+ resolve2(null);
2002
+ return;
2003
+ }
2004
+ let data = "";
2005
+ res.on("data", (chunk) => {
2006
+ data += chunk.toString();
2007
+ });
2008
+ res.on("end", () => {
2009
+ try {
2010
+ resolve2(JSON.parse(data).version);
2011
+ } catch {
2012
+ resolve2(null);
2013
+ }
2014
+ });
2015
+ });
2016
+ req.on("error", () => {
2017
+ resolve2(null);
2018
+ });
2019
+ req.on("timeout", () => {
2020
+ req.destroy();
2021
+ resolve2(null);
2022
+ });
2023
+ });
2024
+ }
2025
+ function detectUpgradeCommand() {
2026
+ const binPath = process.argv[1] || "";
2027
+ if (binPath.includes("/.bun/") || binPath.includes("/bun/")) {
2028
+ return `bun update -g ${PACKAGE_NAME}`;
2029
+ }
2030
+ if (binPath.includes("/.pnpm/") || binPath.includes("/pnpm/")) {
2031
+ return `pnpm update -g ${PACKAGE_NAME}`;
2032
+ }
2033
+ if (binPath.includes("/.yarn/") || binPath.includes("/yarn/")) {
2034
+ return `yarn global upgrade ${PACKAGE_NAME}`;
2035
+ }
2036
+ return `npm update -g ${PACKAGE_NAME}`;
2037
+ }
2038
+ function compareVersions(current, latest) {
2039
+ const a = current.split(".").map(Number);
2040
+ const b = latest.split(".").map(Number);
2041
+ for (let i = 0; i < 3; i++) {
2042
+ if ((a[i] || 0) < (b[i] || 0)) return -1;
2043
+ if ((a[i] || 0) > (b[i] || 0)) return 1;
2044
+ }
2045
+ return 0;
2046
+ }
2047
+ async function checkForUpdates(force) {
2048
+ if (!force && !isFirstUseToday()) return;
2049
+ const current = getCurrentVersion();
2050
+ const latest = await fetchLatestVersion();
2051
+ saveCheckDate();
2052
+ if (latest === null || compareVersions(current, latest) >= 0) return;
2053
+ const cmd = detectUpgradeCommand();
2054
+ const updateLine = `Update available: ${current} \u2192 ${latest}`;
2055
+ const cmdLine = `Run: ${cmd}`;
2056
+ const width = Math.max(updateLine.length, cmdLine.length) + 4;
2057
+ const pad = (text, visLen) => text + " ".repeat(Math.max(0, width - visLen));
2058
+ const border = "\u2500".repeat(width);
2059
+ const empty = " ".repeat(width);
2060
+ console.log("");
2061
+ console.log(` \u250C${border}\u2510`);
2062
+ console.log(` \u2502${empty}\u2502`);
2063
+ console.log(` \u2502 ${pad(`Update available: ${current} \u2192 \x1B[32m${latest}\x1B[0m`, updateLine.length + 2)}\u2502`);
2064
+ console.log(` \u2502${empty}\u2502`);
2065
+ console.log(` \u2502 ${pad(`Run: \x1B[36m${cmd}\x1B[0m`, cmdLine.length + 2)}\u2502`);
2066
+ console.log(` \u2502${empty}\u2502`);
2067
+ console.log(` \u2514${border}\u2518`);
2068
+ console.log("");
2069
+ }
2070
+
1958
2071
  // src/cli.ts
1959
2072
  function printUsage() {
1960
2073
  console.log(`
@@ -1964,9 +2077,10 @@ Usage:
1964
2077
  hotsheet [options]
1965
2078
 
1966
2079
  Options:
1967
- --port <number> Port to run on (default: 4174)
1968
- --data-dir <path> Store data in an alternative location (default: .hotsheet/)
1969
- --help Show this help message
2080
+ --port <number> Port to run on (default: 4174)
2081
+ --data-dir <path> Store data in an alternative location (default: .hotsheet/)
2082
+ --check-for-updates Check for new versions now
2083
+ --help Show this help message
1970
2084
 
1971
2085
  Examples:
1972
2086
  hotsheet
@@ -1976,9 +2090,10 @@ Examples:
1976
2090
  }
1977
2091
  function parseArgs(argv) {
1978
2092
  const args = argv.slice(2);
1979
- let port = 4174;
1980
- let dataDir2 = join6(process.cwd(), ".hotsheet");
2093
+ let port2 = 4174;
2094
+ let dataDir2 = join7(process.cwd(), ".hotsheet");
1981
2095
  let demo = null;
2096
+ let forceUpdateCheck = false;
1982
2097
  for (let i = 0; i < args.length; i++) {
1983
2098
  const arg = args[i];
1984
2099
  if (arg.startsWith("--demo:")) {
@@ -1996,8 +2111,8 @@ function parseArgs(argv) {
1996
2111
  process.exit(0);
1997
2112
  break;
1998
2113
  case "--port":
1999
- port = parseInt(args[++i], 10);
2000
- if (isNaN(port)) {
2114
+ port2 = parseInt(args[++i], 10);
2115
+ if (isNaN(port2)) {
2001
2116
  console.error("Invalid port number");
2002
2117
  process.exit(1);
2003
2118
  }
@@ -2005,13 +2120,16 @@ function parseArgs(argv) {
2005
2120
  case "--data-dir":
2006
2121
  dataDir2 = resolve(args[++i]);
2007
2122
  break;
2123
+ case "--check-for-updates":
2124
+ forceUpdateCheck = true;
2125
+ break;
2008
2126
  default:
2009
2127
  console.error(`Unknown option: ${arg}`);
2010
2128
  printUsage();
2011
2129
  process.exit(1);
2012
2130
  }
2013
2131
  }
2014
- return { port, dataDir: dataDir2, demo };
2132
+ return { port: port2, dataDir: dataDir2, demo, forceUpdateCheck };
2015
2133
  }
2016
2134
  async function main() {
2017
2135
  const parsed = parseArgs(process.argv);
@@ -2019,8 +2137,9 @@ async function main() {
2019
2137
  printUsage();
2020
2138
  process.exit(1);
2021
2139
  }
2022
- const { port, demo } = parsed;
2140
+ const { port: port2, demo, forceUpdateCheck } = parsed;
2023
2141
  let { dataDir: dataDir2 } = parsed;
2142
+ await checkForUpdates(forceUpdateCheck);
2024
2143
  if (demo !== null) {
2025
2144
  const scenario = DEMO_SCENARIOS.find((s) => s.id === demo);
2026
2145
  if (!scenario) {
@@ -2031,12 +2150,12 @@ async function main() {
2031
2150
  }
2032
2151
  process.exit(1);
2033
2152
  }
2034
- dataDir2 = join6(tmpdir(), `hotsheet-demo-${demo}-${Date.now()}`);
2153
+ dataDir2 = join7(tmpdir(), `hotsheet-demo-${demo}-${Date.now()}`);
2035
2154
  console.log(`
2036
2155
  DEMO MODE: ${scenario.label}
2037
2156
  `);
2038
2157
  }
2039
- mkdirSync3(dataDir2, { recursive: true });
2158
+ mkdirSync4(dataDir2, { recursive: true });
2040
2159
  if (demo === null) {
2041
2160
  ensureGitignore(process.cwd());
2042
2161
  }
@@ -2045,13 +2164,13 @@ async function main() {
2045
2164
  if (demo !== null) {
2046
2165
  await seedDemoData(demo);
2047
2166
  }
2048
- initMarkdownSync(dataDir2);
2167
+ initMarkdownSync(dataDir2, port2);
2049
2168
  scheduleAllSync();
2050
2169
  if (demo === null) {
2051
2170
  await cleanupAttachments();
2052
2171
  }
2053
2172
  console.log(` Data directory: ${dataDir2}`);
2054
- await startServer(port, dataDir2);
2173
+ await startServer(port2, dataDir2);
2055
2174
  }
2056
2175
  main().catch((err) => {
2057
2176
  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 le=Object.defineProperty;var de=(e,t,i)=>t in e?le(e,t,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[t]=i;var j=(e,t,i)=>de(e,typeof t!="symbol"?t+"":t,i);function F(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function V(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 ce=new Set(["area","base","br","col","embed","hr","img","input","link","meta","source","track","wbr"]);function D(e){return e==null||typeof e=="boolean"?"":e instanceof h?e.__html:typeof e=="string"?F(e):typeof e=="number"?String(e):Array.isArray(e)?e.map(D).join(""):""}function ue(e,t){if(t==null||t===!1)return"";if(t===!0)return` ${e}`;let i=e==="className"?"class":e==="htmlFor"?"for":e,s;return t instanceof h?s=t.__html:typeof t=="number"?s=String(t):typeof t=="string"?s=V(t):s="",` ${i}="${s}"`}function l(e,t){if(typeof e=="function")return e(t);let{children:i,...s}=t,a=Object.entries(s).map(([d,r])=>ue(d,r)).join("");if(ce.has(e))return new h(`<${e}${a}>`);let o=i!=null?D(i):"";return new h(`<${e}${a}>${o}</${e}>`)}function L({children:e}){return new h(e!=null?D(e):"")}function y(e){let t=document.createElement("template");return t.innerHTML=e.toString(),t.content.firstElementChild}function W(e){document.getElementById("network-error-popup")?.remove();let t=y(l("div",{id:"network-error-popup",className:"error-popup",children:l("div",{className:"error-popup-content",children:[l("strong",{children:"Connection Error"}),l("p",{children:e}),l("button",{children:k("Dismiss")})]})}));t.querySelector("button").addEventListener("click",()=>t.remove()),document.body.appendChild(t)}async function c(e,t={}){try{return(await fetch("/api"+e,{headers:t.body!==void 0?{"Content-Type":"application/json"}:{},method:t.method,body:t.body!==void 0?JSON.stringify(t.body):void 0})).json()}catch(i){throw W("Unable to reach the server. It may have been stopped."),i}}async function G(e,t){try{let i=new FormData;return i.append("file",t),(await fetch("/api"+e,{method:"POST",body:i})).json()}catch(i){throw W("Unable to reach the server. It may have been stopped."),i}}var pe={detail_position:"side",detail_width:360,detail_height:300,trash_cleanup_days:3,verified_cleanup_days:30},n={tickets:[],selectedIds:new Set,lastClickedId:null,activeTicketId:null,view:"all",sortBy:"created",sortDir:"desc",search:"",settings:{...pe}},me={issue:"#6b7280",bug:"#ef4444",feature:"#22c55e",requirement_change:"#f97316",task:"#3b82f6",investigation:"#8b5cf6"},fe={issue:"ISS",bug:"BUG",feature:"FEA",requirement_change:"REQ",task:"TSK",investigation:"INV"},ye={highest:"\u2B06\u2B06",high:"\u2B06",default:"\u2014",low:"\u2B07",lowest:"\u2B07\u2B07"},ge={highest:"#ef4444",high:"#f97316",default:"#6b7280",low:"#3b82f6",lowest:"#94a3b8"},he={not_started:"\u25CB",started:"\u25D4",completed:"\u2713",verified:"svg"},Y='<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 7 17l-5-5"/><path d="m22 10-9.5 9.5-2-2"/></svg>';function b(e){return me[e]||"#6b7280"}function S(e){return fe[e]||"ISS"}function J(e){return ye[e]||"\u2014"}function A(e){return ge[e]||"#6b7280"}function X(e){return he[e]||"\u25CB"}function x(e){n.activeTicketId=e,ee(e)}function Q(){n.selectedIds.clear(),n.activeTicketId=null;let e=new CustomEvent("hotsheet:render");document.dispatchEvent(e)}function Z(){let e=n.view==="trash",t=document.getElementById("detail-panel"),i=document.getElementById("detail-resize-handle");if(n.selectedIds.size===1&&!e){let s=Array.from(n.selectedIds)[0];t.style.display="flex",i&&(i.style.display=""),n.activeTicketId!==s&&(n.activeTicketId=s,ee(s))}else n.activeTicketId!=null&&(n.activeTicketId=null),t.style.display="none",i&&(i.style.display="none")}async function ee(e){let t=await c(`/tickets/${e}`);if(n.activeTicketId!==e)return;document.getElementById("detail-ticket-number").textContent=t.ticket_number,document.getElementById("detail-title").value=t.title,document.getElementById("detail-category").value=t.category,document.getElementById("detail-priority").value=t.priority,document.getElementById("detail-status").value=t.status,document.getElementById("detail-upnext").checked=t.up_next,document.getElementById("detail-details").value=t.details;let i=document.getElementById("detail-attachments");t.attachments.length>0?i.innerHTML=l(L,{children:t.attachments.map(r=>l("div",{className:"attachment-item",children:[l("span",{className:"attachment-name",children:r.original_filename}),l("button",{className:"attachment-delete","data-att-id":String(r.id),title:"Remove",children:k("&times;")})]}))}).toString():i.innerHTML="";let s=document.getElementById("detail-notes-section"),a=document.getElementById("detail-notes"),o=ve(t.notes);o.length>0?(s.style.display="",a.innerHTML=l(L,{children:o.map(r=>l("div",{className:"note-entry",children:[r.created_at?l("div",{className:"note-timestamp",children:new Date(r.created_at).toLocaleString()}):null,l("div",{className:"note-text",children:r.text})]}))}).toString()):(s.style.display="none",a.innerHTML="");let d=document.getElementById("detail-meta");d.innerHTML=l(L,{children:[l("div",{children:["Created: ",new Date(t.created_at).toLocaleString()]}),l("div",{children:["Updated: ",new Date(t.updated_at).toLocaleString()]}),t.completed_at?l("div",{children:["Completed: ",new Date(t.completed_at).toLocaleString()]}):null,t.verified_at?l("div",{children:["Verified: ",new Date(t.verified_at).toLocaleString()]}):null]}).toString()}function ve(e){if(!e||e==="")return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return e.trim()?[{text:e,created_at:""}]:[]}async function te(){try{let e=await c("/stats"),t=document.getElementById("status-bar");t&&(t.textContent=`${e.total} tickets \xB7 ${e.open} open \xB7 ${e.up_next} up next`)}catch{}}function N(e){let t=document.getElementById("content-area");t.classList.remove("detail-side","detail-bottom"),t.classList.add(e==="bottom"?"detail-bottom":"detail-side")}function P(){let e=document.getElementById("detail-panel");n.settings.detail_position==="bottom"?(e.style.width="",e.style.height=`${n.settings.detail_height}px`):(e.style.height="",e.style.width=`${n.settings.detail_width}px`)}function ne(){let e=document.getElementById("detail-resize-handle"),t=document.getElementById("detail-panel"),i=document.getElementById("content-area"),s=!1;e.addEventListener("mousedown",a=>{a.preventDefault(),s=!0,document.body.style.cursor=n.settings.detail_position==="bottom"?"row-resize":"col-resize",document.body.style.userSelect="none"}),document.addEventListener("mousemove",a=>{if(!s)return;let o=i.getBoundingClientRect();if(n.settings.detail_position==="bottom"){let d=Math.max(150,Math.min(500,o.bottom-a.clientY));n.settings.detail_height=d,t.style.height=`${d}px`}else{let d=Math.max(250,Math.min(600,o.right-a.clientX));n.settings.detail_width=d,t.style.width=`${d}px`}}),document.addEventListener("mouseup",()=>{s&&(s=!1,document.body.style.cursor="",document.body.style.userSelect="",n.settings.detail_position==="bottom"?c("/settings",{method:"PATCH",body:{detail_height:String(n.settings.detail_height)}}):c("/settings",{method:"PATCH",body:{detail_width:String(n.settings.detail_width)}}))})}function _(e,t){let i=t.getBoundingClientRect(),s=e.getBoundingClientRect(),a=window.innerWidth,o=window.innerHeight,d=i.left;d+s.width>a-8&&(d=i.right-s.width),d<8&&(d=8);let r=i.bottom+4;r+s.height>o-8&&(r=i.top-s.height-4),r<8&&(r=8),e.style.left=`${d}px`,e.style.top=`${r}px`}function H(e,t){let i=y(l("div",{className:"dropdown-menu",style:"visibility:hidden;top:0;left:0",children:t.map(r=>l("button",{className:`dropdown-item${r.active?" active":""}`,"data-key":r.key,children:[r.color?l("span",{className:"dropdown-dot",style:`background-color:${r.color}`}):null,l("span",{className:"dropdown-label",children:r.label}),r.shortcut?l("kbd",{className:"dropdown-kbd",children:r.shortcut}):null]}))}));i.querySelectorAll(".dropdown-item").forEach((r,u)=>{r.addEventListener("click",()=>{t[u].action(),i.remove()})});function a(r){let u=t.find(p=>r.key.toLowerCase()===p.key.toLowerCase());u?(r.preventDefault(),r.stopPropagation(),u.action(),o()):r.key==="Escape"&&(r.preventDefault(),o())}function o(){i.remove(),document.removeEventListener("keydown",a,!0),document.removeEventListener("click",d)}function d(){o()}return document.addEventListener("keydown",a,!0),setTimeout(()=>{document.addEventListener("click",d)},0),i}function C(){document.querySelectorAll(".dropdown-menu").forEach(e=>{e.remove()})}var $=null,E=!1,I=null,R="",M=[{key:"i",value:"issue",label:"Issue"},{key:"b",value:"bug",label:"Bug"},{key:"f",value:"feature",label:"Feature"},{key:"r",value:"requirement_change",label:"Req Change"},{key:"k",value:"task",label:"Task"},{key:"g",value:"investigation",label:"Investigation"}],O=[{key:"1",value:"highest",label:"Highest"},{key:"2",value:"high",label:"High"},{key:"3",value:"default",label:"Default"},{key:"4",value:"low",label:"Low"},{key:"5",value:"lowest",label:"Lowest"}];function ke(){let e=document.activeElement;if(!e||!(e instanceof HTMLElement))return null;let t=e.closest(".ticket-row");if(!t)return null;if(t.classList.contains("draft-row"))return"draft";let i=t.dataset.id;return i!==void 0&&i!==""?parseInt(i,10):null}function be(e){e!=null&&(E=!0,e==="draft"?g():document.querySelector(`.ticket-row[data-id="${e}"] .ticket-title-input`)?.focus(),E=!1)}function f(){let e=n.view==="trash",t=ke(),i=null;if(t!=null&&t!=="draft"){let a=document.querySelector(`.ticket-row[data-id="${t}"] .ticket-title-input`);a&&(i=a.value)}let s=document.getElementById("ticket-list");s.innerHTML="",e||s.appendChild(Te()),e&&n.tickets.length===0&&s.appendChild(y(l("div",{className:"ticket-list-empty",children:"Trash is empty"})));for(let a of n.tickets)s.appendChild(e?Le(a):we(a));if(t!=null&&t!=="draft"&&i!=null){let a=document.querySelector(`.ticket-row[data-id="${t}"] .ticket-title-input`);a&&a.value!==i&&(a.value=i)}be(t),B(),te()}function Te(){let e=ae(),t=n.view.startsWith("category:"),i=y(l("div",{className:"ticket-row draft-row",children:[l("span",{className:"ticket-checkbox-spacer"}),l("span",{className:"ticket-status-btn draft-placeholder",children:"\u25CB"}),l("span",{className:"ticket-category-badge draft-badge",style:`background-color:${b(e)}${t?"":";cursor:pointer;opacity:1"}`,children:S(e)}),l("span",{className:"ticket-number draft-number"}),l("input",{type:"text",className:"ticket-title-input draft-input",placeholder:"New ticket...",value:R}),l("span",{className:"ticket-priority-indicator draft-placeholder"}),l("span",{className:"ticket-star draft-placeholder"})]}));if(!t){let a=i.querySelector(".ticket-category-badge");a.addEventListener("click",o=>{o.stopPropagation(),Ie(a)})}let s=i.querySelector(".draft-input");return s.addEventListener("input",()=>{R=s.value}),s.addEventListener("keydown",async a=>{if(a.key==="Enter"&&s.value.trim()){a.preventDefault();let o=s.value.trim();R="",s.value="";let d=Ee();I&&!n.view.startsWith("category:")&&(d.category=I);let r=await c("/tickets",{method:"POST",body:{title:o,defaults:d}});r&&(n.selectedIds.clear(),n.selectedIds.add(r.id)),await m(),g()}else a.key==="ArrowDown"&&(a.preventDefault(),n.tickets.length>0&&document.querySelector(`.ticket-row[data-id="${n.tickets[0].id}"] .ticket-title-input`)?.focus())}),i}function g(){document.querySelector(".draft-row .draft-input")?.focus()}function Ee(){let e=n.view;return e==="up-next"?{up_next:!0}:e==="open"?{}:e==="completed"?{status:"completed"}:e.startsWith("category:")?{category:e.split(":")[1]}:e.startsWith("priority:")?{priority:e.split(":")[1]}:{}}function ae(){if(I)return I;let e=n.view;return e.startsWith("category:")?e.split(":")[1]:"issue"}function Ie(e){C();let i=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",s=ae(),a=H(e,M.map(o=>({label:o.label,key:o.key,shortcut:`${i}${o.key.toUpperCase()}`,color:b(o.value),active:s===o.value,action:()=>{I=o.value,f(),g()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function we(e){let t=n.selectedIds.has(e.id),i=e.status==="completed"||e.status==="verified",s=e.status==="verified",a=y(l("div",{className:`ticket-row${t?" selected":""}${i?" completed":""}${e.up_next?" up-next":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("button",{className:`ticket-status-btn${s?" verified":""}`,title:e.status.replace("_"," "),children:s?k(Y):X(e.status)}),l("span",{className:"ticket-category-badge",style:`background-color:${b(e.category)}`,title:e.category,children:S(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("input",{type:"text",className:"ticket-title-input",value:e.title}),l("span",{className:"ticket-priority-indicator",style:`color:${A(e.priority)}`,title:e.priority,children:J(e.priority)}),l("button",{className:`ticket-star${e.up_next?" active":""}`,title:e.up_next?"Remove from Up Next":"Add to Up Next",children:e.up_next?"\u2605":"\u2606"})]}));a.addEventListener("mousedown",p=>{(p.metaKey||p.ctrlKey||p.shiftKey)&&(p.preventDefault(),oe(p,e)&&p.stopPropagation())});let o=a.querySelector(".ticket-checkbox");o.addEventListener("click",p=>p.stopPropagation()),o.addEventListener("change",()=>{o.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,f()}),a.querySelector(".ticket-status-btn").addEventListener("click",p=>{p.stopPropagation(),He(e)});let d=a.querySelector(".ticket-category-badge");d.addEventListener("click",p=>{p.stopPropagation(),De(d,e)});let r=a.querySelector(".ticket-title-input");r.addEventListener("focus",()=>{E||n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,q(),B())}),r.addEventListener("input",()=>{Be(e.id,{title:r.value})}),r.addEventListener("keydown",p=>{Se(p,e,r)});let u=a.querySelector(".ticket-priority-indicator");return u.addEventListener("click",p=>{p.stopPropagation(),Ae(u,e)}),a.querySelector(".ticket-star").addEventListener("click",p=>{p.stopPropagation(),Ce(e)}),a}function Le(e){let t=n.selectedIds.has(e.id),i=e.deleted_at?new Date(e.deleted_at):null,s=y(l("div",{className:`ticket-row trash-row${t?" selected":""}`,"data-id":String(e.id),children:[l("input",{type:"checkbox",className:"ticket-checkbox",checked:t}),l("span",{className:"ticket-category-badge",style:`background-color:${b(e.category)}`,children:S(e.category)}),l("span",{className:"ticket-number",children:e.ticket_number}),l("span",{className:"ticket-title-input trash-title",style:"cursor:default",children:e.title}),l("span",{className:"ticket-number",title:i?`Deleted: ${i.toLocaleString()}`:"",children:i?i.toLocaleDateString():""}),l("button",{className:"btn btn-sm",title:"Restore from trash",children:"Restore"})]}));s.addEventListener("mousedown",o=>{(o.metaKey||o.ctrlKey||o.shiftKey)&&(o.preventDefault(),oe(o,e)&&o.stopPropagation())});let a=s.querySelector(".ticket-checkbox");return a.addEventListener("click",o=>o.stopPropagation()),a.addEventListener("change",()=>{a.checked?n.selectedIds.add(e.id):n.selectedIds.delete(e.id),n.lastClickedId=e.id,f()}),s.querySelector(".trash-title").addEventListener("click",()=>{n.selectedIds.size===1&&n.selectedIds.has(e.id)||(n.selectedIds.clear(),n.selectedIds.add(e.id),n.lastClickedId=e.id,q(),B())}),s.querySelector(".btn").addEventListener("click",async o=>{o.stopPropagation(),await c(`/tickets/${e.id}/restore`,{method:"POST"}),m()}),s}function oe(e,t){let i=e.metaKey||e.ctrlKey,s=e.shiftKey;if(i)n.selectedIds.has(t.id)?n.selectedIds.delete(t.id):n.selectedIds.add(t.id),n.lastClickedId=t.id,f();else if(s&&n.lastClickedId!=null){let a=n.tickets.map(r=>r.id),o=a.indexOf(n.lastClickedId),d=a.indexOf(t.id);if(o!==-1&&d!==-1){let r=Math.min(o,d),u=Math.max(o,d);n.selectedIds.clear();for(let p=r;p<=u;p++)n.selectedIds.add(a[p])}f()}else return!1;return!0}function Se(e,t,i){if(e.key==="Enter")e.preventDefault(),g();else if(e.key==="Backspace"&&i.value==="")e.preventDefault(),Me(t.id);else if(e.key==="ArrowDown"&&e.shiftKey)e.preventDefault(),ie(t.id,1);else if(e.key==="ArrowUp"&&e.shiftKey)e.preventDefault(),ie(t.id,-1);else if(e.key==="ArrowDown")e.preventDefault(),xe(t.id);else if(e.key==="ArrowUp")e.preventDefault(),_e(t.id);else if((e.metaKey||e.ctrlKey)&&!e.altKey&&M.some(s=>s.key===e.key)){e.preventDefault();let s=M.find(a=>a.key===e.key);se(t,"category",s.value)}else if(e.altKey&&!e.metaKey&&!e.ctrlKey&&O.some(s=>s.key===e.key)){e.preventDefault();let s=O.find(a=>a.key===e.key);se(t,"priority",s.value)}}function xe(e){let t=n.tickets.findIndex(i=>i.id===e);t<n.tickets.length-1&&document.querySelector(`.ticket-row[data-id="${n.tickets[t+1].id}"] .ticket-title-input`)?.focus()}function _e(e){let t=n.tickets.findIndex(i=>i.id===e);t>0?document.querySelector(`.ticket-row[data-id="${n.tickets[t-1].id}"] .ticket-title-input`)?.focus():g()}function ie(e,t){let s=n.tickets.findIndex(d=>d.id===e)+t;if(s<0||s>=n.tickets.length)return;let a=n.tickets[s].id;n.selectedIds.add(e),n.selectedIds.has(a)?n.selectedIds.delete(e):n.selectedIds.add(a),E=!0,document.querySelector(`.ticket-row[data-id="${a}"] .ticket-title-input`)?.focus(),E=!1,q(),B()}async function He(e){let i={not_started:"started",started:"completed",completed:"verified",verified:"not_started"}[e.status]||"not_started",s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:i}});Object.assign(e,s),f()}async function Ce(e){if(!e.up_next&&(e.status==="completed"||e.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?"))return;let i=await c(`/tickets/${e.id}`,{method:"PATCH",body:{status:"not_started",up_next:!0}});Object.assign(e,i),f();return}let t=await c(`/tickets/${e.id}/up-next`,{method:"POST"});Object.assign(e,t),f()}async function se(e,t,i){let s=await c(`/tickets/${e.id}`,{method:"PATCH",body:{[t]:i}});Object.assign(e,s),f()}async function Me(e){let t=n.tickets.findIndex(i=>i.id===e);if(await c(`/tickets/${e}`,{method:"DELETE"}),n.tickets=n.tickets.filter(i=>i.id!==e),n.selectedIds.delete(e),f(),t>0&&n.tickets.length>0){let i=Math.min(t-1,n.tickets.length-1);document.querySelector(`.ticket-row[data-id="${n.tickets[i].id}"] .ticket-title-input`)?.focus()}else g()}function Be(e,t){$&&clearTimeout($),$=setTimeout(()=>{c(`/tickets/${e}`,{method:"PATCH",body:t})},300)}function De(e,t){C();let s=navigator.platform.includes("Mac")?"\u2318":"Ctrl+",a=H(e,M.map(o=>({label:o.label,key:o.key,shortcut:`${s}${o.key.toUpperCase()}`,color:b(o.value),active:t.category===o.value,action:async()=>{let d=await c(`/tickets/${t.id}`,{method:"PATCH",body:{category:o.value}});Object.assign(t,d),f()}})));document.body.appendChild(a),_(a,e),a.style.visibility=""}function Ae(e,t){C();let i=H(e,O.map(s=>({label:s.label,key:s.key,shortcut:`Alt+${s.key}`,color:A(s.value),active:t.priority===s.value,action:async()=>{let a=await c(`/tickets/${t.id}`,{method:"PATCH",body:{priority:s.value}});Object.assign(t,a),f()}})));document.body.appendChild(i),_(i,e),i.style.visibility=""}function q(){document.querySelectorAll(".ticket-row[data-id]").forEach(e=>{let t=parseInt(e.dataset.id,10),i=e.querySelector(".ticket-checkbox");n.selectedIds.has(t)?(e.classList.add("selected"),i&&(i.checked=!0)):(e.classList.remove("selected"),i&&(i.checked=!1))})}function B(){let e=n.selectedIds.size,t=n.tickets.length,i=e>0,s=n.view==="trash",a=document.getElementById("batch-select-all");a.checked=t>0&&e===t,a.indeterminate=e>0&&e<t,document.getElementById("batch-count").textContent=i?`${e} selected`:"";let o=["batch-category","batch-priority","batch-status","batch-upnext","batch-delete"];for(let v of o){let w=document.getElementById(v);w.style.display=s?"none":"",s||(w.disabled=!i)}let d=document.getElementById("batch-restore"),r=document.getElementById("batch-empty-trash");if(s){let v=document.getElementById("batch-toolbar");d||(d=y(l("button",{id:"batch-restore",className:"btn btn-sm",children:"Restore"})),d.addEventListener("click",async()=>{await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"restore"}}),n.selectedIds.clear(),m()}),v.insertBefore(d,document.getElementById("batch-count"))),d.disabled=!i,d.style.display="",r||(r=y(l("button",{id:"batch-empty-trash",className:"btn btn-sm btn-danger",children:"Empty Trash"})),r.addEventListener("click",async()=>{confirm("Permanently delete all items in trash? This cannot be undone.")&&(await c("/trash/empty",{method:"POST"}),n.selectedIds.clear(),m())}),v.insertBefore(r,document.getElementById("batch-count"))),r.disabled=t===0,r.style.display=""}else d&&(d.style.display="none"),r&&(r.style.display="none");let u=document.querySelector(".batch-star-icon"),p=document.getElementById("batch-upnext");if(!s&&u&&i){let v=n.tickets.filter(T=>n.selectedIds.has(T.id)),w=v.every(T=>T.up_next),re=v.every(T=>!T.up_next);w?(u.textContent="\u2605",p.classList.add("active"),p.classList.remove("mixed")):re?(u.textContent="\u2606",p.classList.remove("active","mixed")):(u.innerHTML=l("span",{className:"star-mixed-wrap",children:[l("span",{className:"star-mixed-fill",children:"\u2605"}),"\u2606"]}).toString(),p.classList.remove("active"),p.classList.add("mixed"))}else u&&(u.textContent="\u2606",p.classList.remove("active","mixed"));Z()}async function m(){let e=new URLSearchParams;n.view==="trash"?e.set("status","deleted"):n.view==="up-next"?e.set("up_next","true"):n.view==="open"?e.set("status","open"):n.view==="completed"?e.set("status","completed"):n.view==="verified"?e.set("status","verified"):n.view.startsWith("category:")?e.set("category",n.view.split(":")[1]):n.view.startsWith("priority:")&&e.set("priority",n.view.split(":")[1]),n.search&&e.set("search",n.search),e.set("sort_by",n.sortBy),e.set("sort_dir",n.sortDir);let t=e.toString();n.tickets=await c(`/tickets${t?"?"+t:""}`),f()}async function Ne(){await Pe(),await m(),Oe(),qe(),Ke(),Ue(),ze(),Ve(),$e(),Re(),ne(),We(),document.addEventListener("hotsheet:render",()=>f()),g()}async function Pe(){try{let e=await c("/settings");(e.detail_position==="side"||e.detail_position==="bottom")&&(n.settings.detail_position=e.detail_position),e.detail_width&&(n.settings.detail_width=parseInt(e.detail_width,10)||360),e.detail_height&&(n.settings.detail_height=parseInt(e.detail_height,10)||300),e.trash_cleanup_days&&(n.settings.trash_cleanup_days=parseInt(e.trash_cleanup_days,10)||3),e.verified_cleanup_days&&(n.settings.verified_cleanup_days=parseInt(e.verified_cleanup_days,10)||30)}catch{}N(n.settings.detail_position),P()}function $e(){let e=document.getElementById("settings-overlay"),t=document.getElementById("settings-close");document.getElementById("settings-btn").addEventListener("click",()=>{document.getElementById("settings-detail-position").value=n.settings.detail_position,document.getElementById("settings-trash-days").value=String(n.settings.trash_cleanup_days),document.getElementById("settings-verified-days").value=String(n.settings.verified_cleanup_days),e.style.display="flex"}),t.addEventListener("click",()=>{e.style.display="none"}),e.addEventListener("click",u=>{u.target===e&&(e.style.display="none")});let s=document.getElementById("settings-detail-position");s.addEventListener("change",()=>{n.settings.detail_position=s.value,N(n.settings.detail_position),P(),c("/settings",{method:"PATCH",body:{detail_position:s.value}})});let a=document.getElementById("settings-trash-days"),o=null;a.addEventListener("input",()=>{o&&clearTimeout(o),o=setTimeout(()=>{let u=Math.max(1,parseInt(a.value,10)||3);a.value=String(u),n.settings.trash_cleanup_days=u,c("/settings",{method:"PATCH",body:{trash_cleanup_days:String(u)}})},500)});let d=document.getElementById("settings-verified-days"),r=null;d.addEventListener("input",()=>{r&&clearTimeout(r),r=setTimeout(()=>{let u=Math.max(1,parseInt(d.value,10)||30);d.value=String(u),n.settings.verified_cleanup_days=u,c("/settings",{method:"PATCH",body:{verified_cleanup_days:String(u)}})},500)})}function Re(){let e=document.getElementById("copy-prompt-section"),t=document.getElementById("copy-prompt-btn"),i=document.getElementById("copy-prompt-label"),s=document.getElementById("copy-prompt-icon"),a="";c("/worklist-info").then(o=>{a=o.prompt,e.style.display="",o.skillCreated&&console.log("Hot Sheet: Created /hotsheet skill in .claude/skills/hotsheet/")}),t.addEventListener("click",()=>{a!==""&&navigator.clipboard.writeText(a).then(()=>{i.textContent="Copied!",s.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>',setTimeout(()=>{i.textContent="Copy AI prompt",s.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>'},1500)})})}function Oe(){let e=document.querySelectorAll(".sidebar-item[data-view]");e.forEach(t=>{t.addEventListener("click",()=>{e.forEach(i=>{i.classList.remove("active")}),t.classList.add("active"),n.view=t.dataset.view,n.selectedIds.clear(),m()})})}function qe(){let e=document.getElementById("sort-select");e.addEventListener("change",()=>{let[t,i]=e.value.split(":");n.sortBy=t,n.sortDir=i,m()})}var K=null;function Ke(){let e=document.getElementById("search-input");e.addEventListener("input",()=>{K&&clearTimeout(K),K=setTimeout(()=>{n.search=e.value,m()},200)}),e.addEventListener("keydown",t=>{t.key==="Escape"&&(e.value="",n.search="",m())})}function Ue(){let e=document.getElementById("batch-category");e.addEventListener("change",async()=>{e.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"category",value:e.value}}),e.value="",m())});let t=document.getElementById("batch-priority");t.addEventListener("change",async()=>{t.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"priority",value:t.value}}),t.value="",m())});let i=document.getElementById("batch-status");i.addEventListener("change",async()=>{i.value&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"status",value:i.value}}),i.value="",m())}),document.getElementById("batch-upnext").addEventListener("click",async()=>{let s=n.tickets.filter(d=>n.selectedIds.has(d.id)),o=!s.every(d=>d.up_next);if(o){let d=s.filter(r=>r.status==="completed"||r.status==="verified");if(d.length>0){if(!confirm("Some selected tickets are already done. Would you like to reopen them and add them to Up Next?"))return;await c("/tickets/batch",{method:"POST",body:{ids:d.map(r=>r.id),action:"status",value:"not_started"}})}}await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:o}}),m()}),document.getElementById("batch-delete").addEventListener("click",async()=>{let s=n.selectedIds.size;confirm(`Delete ${s} ticket(s)?`)&&(await c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"delete"}}),n.selectedIds.clear(),m())}),document.getElementById("batch-select-all").addEventListener("change",s=>{if(s.target.checked)for(let o of n.tickets)n.selectedIds.add(o.id);else n.selectedIds.clear();f()})}var U=null;function ze(){document.getElementById("detail-close").addEventListener("click",Q);let e=["detail-title","detail-details"];for(let i of e){let s=document.getElementById(i);s.addEventListener("input",()=>{U&&clearTimeout(U),U=setTimeout(()=>{if(n.activeTicketId==null)return;let a=i.replace("detail-","");c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:s.value}}).then(()=>{m()})},300)})}let t=["detail-category","detail-priority","detail-status"];for(let i of t){let s=document.getElementById(i);s.addEventListener("change",async()=>{if(n.activeTicketId==null)return;let a=i.replace("detail-","");await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{[a]:s.value}}),m()})}document.getElementById("detail-upnext").addEventListener("change",async()=>{if(n.activeTicketId==null)return;let i=n.tickets.find(a=>a.id===n.activeTicketId),s=document.getElementById("detail-upnext");if(s.checked&&i&&(i.status==="completed"||i.status==="verified")){if(!confirm("This ticket is already done. Would you like to reopen it and add it to Up Next?")){s.checked=!1;return}await c(`/tickets/${n.activeTicketId}`,{method:"PATCH",body:{status:"not_started",up_next:!0}})}else await c(`/tickets/${n.activeTicketId}/up-next`,{method:"POST"});m(),x(n.activeTicketId)}),document.getElementById("detail-file-input").addEventListener("change",async i=>{let s=i.target,a=s.files?.[0];!a||n.activeTicketId==null||(await G(`/tickets/${n.activeTicketId}/attachments`,a),s.value="",x(n.activeTicketId),m())}),document.getElementById("detail-attachments").addEventListener("click",async i=>{let a=i.target.closest(".attachment-delete");if(a===null)return;let o=a.dataset.attId;o===void 0||o===""||(await c(`/attachments/${o}`,{method:"DELETE"}),n.activeTicketId!=null&&x(n.activeTicketId))})}function je(e){if(!e||e==="")return[];try{let t=JSON.parse(e);if(Array.isArray(t))return t}catch{}return e.trim()?[{text:e,created_at:""}]:[]}function Fe(e){let t=[];t.push(`${e.ticket_number}: ${e.title}`),e.details.trim()&&(t.push(""),t.push(e.details.trim()));let i=je(e.notes);if(i.length>0){t.push("");for(let s of i)t.push(`- ${s.text}`)}return t.join(`
2
+ `)}function Ve(){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(),f());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);f();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(()=>{m()});return}}c("/tickets/batch",{method:"POST",body:{ids:Array.from(n.selectedIds),action:"up_next",value:d}}).then(()=>{m()})}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(r=>n.selectedIds.has(r.id)).map(Fe).join(`
3
+
4
+ `);navigator.clipboard.writeText(d);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 z=0;function We(){async function e(){try{let t=await c(`/poll?version=${z}`);t.version>z&&(z=t.version,m())}catch{await new Promise(t=>setTimeout(t,5e3))}setTimeout(e,100)}e()}Ne();})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hotsheet",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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",