whose-turn 1.0.0-alpha.2 → 1.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +9 -6
- package/dist/assets/bem-COKTjsky.js +1 -0
- package/dist/assets/index-CuyWocyb.js +3 -0
- package/dist/assets/index-p8D7awn0.css +1 -0
- package/dist/assets/{report-Xynr6yGL.js → report-BtGnm9ax.js} +1 -1
- package/dist/index.html +3 -3
- package/dist/report.html +2 -2
- package/lib/api.mjs +11 -4
- package/lib/errors.mjs +2 -0
- package/lib/feedback.mjs +5 -2
- package/lib/update.mjs +134 -0
- package/package.json +1 -1
- package/server.mjs +19 -3
- package/dist/assets/bem-puYdOas_.js +0 -1
- package/dist/assets/index-DR8StBp8.js +0 -3
- package/dist/assets/index-d0wlPvUz.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable changes to whose-turn are recorded here. The format follows [Keep a
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.0.0-alpha.3] - 2026-11-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Self-update of the installed package. Every ten minutes the server reads the npm dist-tags (`latest`, which every publish moves) and, when a different version is out, reports it as `update` in `/api/summary`, `/api/state` and the stream, and raises one desktop notification per version. `POST /api/update` installs it into the package's own location and restarts the server; the status bar shows "<version> is out · Update", then "Updating…", then "Updated to <version>". A checkout is never offered an update. New reasons `UPDATE_IN_PROGRESS` (409) and `UPDATE_UNAVAILABLE` (412). RMA-231.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- The window opens its event stream again. Since RMA-197 the store waited for a `CLOSED` status that VueUse never reports before the first open, so the window never subscribed to `/api/stream` and picked up new polls only on a reload.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- The release workflow publishes through npm trusted publishing (OIDC) instead of a stored token. RMA-235.
|
|
17
|
+
|
|
7
18
|
## [1.0.0-alpha.2] - 2026-11-03
|
|
8
19
|
|
|
9
20
|
### Removed
|
package/README.md
CHANGED
|
@@ -16,9 +16,9 @@ npm ci && npm run build
|
|
|
16
16
|
./install.sh && ./open.sh
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
`install.sh` makes the monitor start at login and restart if it stops (a launch agent on macOS, a systemd user unit on Linux; on Windows it prints the one `schtasks` line to run). `open.sh` opens the window as its own app window, or in a browser tab with `--browser`. The window listens on port 8765; `WHOSE_TURN_PORT` changes that.
|
|
19
|
+
`install.sh` makes the monitor start at login and restart if it stops (a launch agent on macOS, a systemd user unit on Linux; on Windows it prints the one `schtasks` line to run). `open.sh` opens the window as its own app window, or in a browser tab with `--browser`. The window listens on port 8765; `WHOSE_TURN_PORT` changes that. A clone updates with `git pull` and a rebuild; it is never offered the in-app update.
|
|
20
20
|
|
|
21
|
-
With Claude Code, the plugin installs and starts all of this for you, with no clone and no build (see [Claude Code](#claude-code)):
|
|
21
|
+
With Claude Code, the plugin installs and starts all of this for you, with no clone and no build (see [Claude Code](#claude-code)). That install updates itself: the server checks npm every ten minutes, tells you about a new version with a desktop notification and a chip in the status bar, and installs it and restarts on one click (`POST /api/update`).
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
24
|
claude plugin marketplace add aquivalabs/BladeForge
|
|
@@ -48,6 +48,7 @@ Nothing that names an employer, an account or a team process lives in this repos
|
|
|
48
48
|
| `port`, `poll_seconds`, `history_days` | the port, how often to poll, how far back the feed reaches |
|
|
49
49
|
| `stale_minutes`, `failing_polls` | health alerts: no data from any source for this many minutes (default 15), or one source failing this many polls in a row (default 3) |
|
|
50
50
|
| `feedback.workspace`, `team`, `project` | where the "to the backlog" button and the `whose_turn_feedback` MCP tool file bugs and improvements about the monitor: a Linear workspace slug, team key and project name; the key with issue-creation rights goes to the secret store under `feedback.token_service` (default `whose-turn-feedback`) and the workspace slug |
|
|
51
|
+
| `feedback.assign` | `false` leaves a filed issue unassigned for triage; by default it is assigned to the owner of the feedback key, so it shows up on that person's own dashboard |
|
|
51
52
|
| `feedback.composer` | the user's own agent that turns a remark into an issue: `{command, args}` of a CLI that reads the prompt on stdin and prints JSON (`{title, kind, body}`, or a wrapper with a `result` string that contains it); default `claude -p --output-format json --model claude-haiku-5-5`, so Claude Code on the user's subscription; `codex exec` or anything alike works the same way |
|
|
52
53
|
| `feedback.bug_words` | extra words, in any language, that make the fallback composer (used when the agent CLI gives no usable answer) call a remark a bug; matched as stems at a word start; the English defaults bug, broken, crash, error, fail, wrong always apply |
|
|
53
54
|
| `workspaces[].id`, `name` | a short id used in the API and on disk (lowercase letters, digits and hyphens, up to 40, starting with a letter or digit; a missing id becomes `ws<n>`) and the display name |
|
|
@@ -173,12 +174,13 @@ The monitor also alerts on its own polling: stale data, or a source that keeps f
|
|
|
173
174
|
|
|
174
175
|
## API, `127.0.0.1:<port>`
|
|
175
176
|
|
|
176
|
-
Requests must carry the local `Host`; a POST must be `application/json`. An error is a `google.rpc.Status` envelope: `{"error":{"code","status","message","details":[{"@type":"ErrorInfo","reason"}]}}`; branch on `reason`. The reasons: `NOT_FOUND` (unknown path, 404), `METHOD_NOT_ALLOWED` (405 with an `Allow: GET, POST` header, a method other than GET or POST), `NO_SUCH_WORKSPACE`, `NO_SUCH_TODO` (404), `NO_WORKSPACE` (412, a request that needs a workspace while none is configured; `/api/state` still answers 200 with an empty snapshot carrying `problems`, which the first-run card reads), `NOT_CONFIGURED` (412, feedback is not set up), `UPSTREAM_FAILED` (502, Linear failed or answered an error; the HTTP status is in the `ErrorInfo` metadata `upstream_status`), `RATE_LIMITED` (429), `UNKNOWN_ACTION`, `INVALID_BODY`, `INVALID_ARGUMENT` (an empty to-do or remark names the field `text` in a `BadRequest` detail), `INVALID_CONFIG` (400), `FORBIDDEN_ORIGIN` (403) and `INTERNAL` (500, a fixed message; the raw exception text appears only in a `DebugInfo` detail when the server runs with `WHOSE_TURN_DEBUG=1`, and always in the server log).
|
|
177
|
+
Requests must carry the local `Host`; a POST must be `application/json`. An error is a `google.rpc.Status` envelope: `{"error":{"code","status","message","details":[{"@type":"ErrorInfo","reason"}]}}`; branch on `reason`. The reasons: `NOT_FOUND` (unknown path, 404), `METHOD_NOT_ALLOWED` (405 with an `Allow: GET, POST` header, a method other than GET or POST), `NO_SUCH_WORKSPACE`, `NO_SUCH_TODO` (404), `NO_WORKSPACE` (412, a request that needs a workspace while none is configured; `/api/state` still answers 200 with an empty snapshot carrying `problems`, which the first-run card reads), `NOT_CONFIGURED` (412, feedback is not set up), `UPSTREAM_FAILED` (502, Linear failed or answered an error; the HTTP status is in the `ErrorInfo` metadata `upstream_status`), `RATE_LIMITED` (429), `UNKNOWN_ACTION`, `INVALID_BODY`, `INVALID_ARGUMENT` (an empty to-do or remark names the field `text` in a `BadRequest` detail), `INVALID_CONFIG` (400), `UPDATE_IN_PROGRESS` (409, an update is already installing), `UPDATE_UNAVAILABLE` (412, nothing to update: a checkout, or already the latest version), `FORBIDDEN_ORIGIN` (403) and `INTERNAL` (500, a fixed message; the raw exception text appears only in a `DebugInfo` detail when the server runs with `WHOSE_TURN_DEBUG=1`, and always in the server log).
|
|
177
178
|
|
|
178
179
|
- `GET /api/workspaces`: `[{id, name, key_pattern, connectors:[{id, type, label}], open_moves, theme}]`; `theme` is `{light, dark}`, each a map of CSS variable to value, or `null` without a seed. `/api/summary` leaves `theme` out.
|
|
179
180
|
- `GET /api/state?ws=<id>`: one workspace, default the first: `turn` (the rows, each with `group`, `rank`, `next`, `why`, `url`, `ticket_url`, `source`, `status_group` (`backlog`, `passed`, `code_review`, `qa`, `on_hold` or empty) and `handled`), `counts` (`mine`, `others`, `failing`, `done`: the four header tiles), `feed`, `marks`, `todos` (each with `ticket_url`), `errors`, `health`, `polled_at`, `me` (your name per source), `workspace`, plus `workspaces` and `problems`. With `&full=1` the merged raw collections come too: `tickets`, `comments`, `prs`, `activity`, `asks`, `events`.
|
|
180
181
|
- `GET /api/summary`: across all workspaces: `moves` (each with `ws`), `new_events`, `todos`, `polled_at`, `errors`, `health` (`{ok, alerts}`, alerts as text prefixed with the workspace name when there are several), and `workspaces` with the same per workspace, where `health` is `{ok, stale, fresh_at, failing:[{source, polls}], alerts:[{id, text}]}`.
|
|
181
|
-
- `GET /api/stream`: SSE. One `data:` event per poll per workspace, `{ws, version, polled_at, open_moves}`. The window reloads state when its workspace's `version` changes.
|
|
182
|
+
- `GET /api/stream`: SSE. One `data:` event per poll per workspace, `{ws, version, polled_at, open_moves}`. The window reloads state when its workspace's `version` changes. The update status arrives as `{update}`, once on connect and on every change.
|
|
183
|
+
- `/api/summary` and `/api/state` carry `update: {current, latest, available, state, reason?}`. The server checks `https://registry.npmjs.org/-/package/whose-turn/dist-tags` every ten minutes (ETag-cached) and follows the `latest` dist-tag, which every publish moves (prereleases included during the alpha); `available` is true when that tag names another version and the server runs from the plugin's install, `<data>/app/node_modules/whose-turn`. `state` is `idle`, `installing`, `restarting` or `failed` (with npm's last lines in `reason`). A new version raises one desktop notification. `POST /api/update` runs `npm install --prefix <data>/app --no-audit --no-fund --no-save whose-turn@<version>` and restarts: under the launch agent it exits and the agent starts the new copy; without one it frees the port, starts the new `server.mjs` detached (output to `<data>/server.log`) and exits. A failed install leaves the running copy as it was. A second `POST` while one runs is refused with `UPDATE_IN_PROGRESS`; one with nothing to update with `UPDATE_UNAVAILABLE`. This is the expected, announced way an install stays current, not a silent update.
|
|
182
184
|
- A read without `ws` answers for the first workspace; a write (`/api/marks`, `/api/todos`, an inbox hand-off) without `ws` is refused with `INVALID_ARGUMENT`, because it would land silently in the wrong workspace (RMA-115).
|
|
183
185
|
- `POST /api/marks` takes `{ws, seen:[ids], base:ms, handled:{row_id: iso|null}, notified:[ids]}`; `seen` must be strings, `base` a number not in the future, `handled` values ISO dates or null, else `INVALID_ARGUMENT` names the field. It answers `{marks, turn, counts}`, recomputed, so the window updates without a reload.
|
|
184
186
|
- `POST /api/todos` takes `{ws, action, …}`: `add {text, key?, by?}`; `done`, `undo`, `delete` with `{id}`; `edit {id, text}`.
|
|
@@ -205,7 +207,7 @@ Requests must carry the local `Host`; a POST must be `application/json`. An erro
|
|
|
205
207
|
- The AI button, a small sparkle on every row, on a feed group head (the whole thread of that ticket), on an expanded feed item, under the title and in the top bar, copies that item, the thread, the workspace or everything as Markdown with its context, ready to paste into any assistant.
|
|
206
208
|
- Feed: grouped by ticket by default, the ticket with the newest event on top; a group head is two short lines, the key with its summary, then the event count or "N new", the first two kinds of event plus "+n" and the time on the right, and a ticket that is your move carries an amber bar on its left edge; a group opens into its events. The source filter and the By ticket / Flat switch share one row, the unread total sits beside the title. A search box filters by any words across key, author, kind and text, and opens the matching groups. "Flat" shows the plain newest-first list. One filter button per connector entry, named by its `label`, else by its `id` when two entries share a type, else by the type; each item's round badge shows the initials of that name (GH for a GitHub entry under its own name). A pull request always reads `repo#number` with its title, with or without a ticket key. Unread items highlighted until clicked plus "Mark all read". Only written things expand. "Open formatted" renders the Markdown safely from DOM nodes, never innerHTML. A "@you" tag marks mentions.
|
|
207
209
|
- To do: an input with an Add button, checkboxes, an Open/Done switch in the head; ticket keys become links. Claude adds items through the API.
|
|
208
|
-
- Status line: live dot, "last check Xs ago" ("Stale · last data X ago" with a red dot when the data is old; "No data: <sources> failing" with a red dot when every source fails, an amber dot when only some do), Refresh now, "Open as app" in a browser tab or "Open in browser" in the app window, and a banner for source, config and health alerts. `open.sh` opens the app window on `/?mode=app`, which is how the page knows it is one; a reloaded app window keeps that, a tab stays a tab. The app window reports its size after a resize and the next one opens at it.
|
|
210
|
+
- Status line: live dot, "last check Xs ago" ("Stale · last data X ago" with a red dot when the data is old; "No data: <sources> failing" with a red dot when every source fails, an amber dot when only some do), Refresh now, the update chip when the install has a newer version waiting ("1.0.0-alpha.3 is out · Update", then "Updating…" and "Updated to …" once the stream reconnects to the restarted server; "Update failed · Retry" with npm's message as its tooltip), "Open as app" in a browser tab or "Open in browser" in the app window, and a banner for source, config and health alerts. `open.sh` opens the app window on `/?mode=app`, which is how the page knows it is one; a reloaded app window keeps that, a tab stays a tab. The app window reports its size after a resize and the next one opens at it.
|
|
209
211
|
- A rail down the left edge lists the workspaces, like a browser's side tabs: icons only, names on hover. Each icon is generated from the workspace id and tinted by its palette, with the open "your move" count as a badge. The choice is remembered in the browser and `?ws=<id>` opens one directly. Drag an icon up or down to reorder the workspaces; the order is saved to the config file.
|
|
210
212
|
- "Report" in the top bar opens the backlog sheet: one text field, write the problem or the wish in your own words; an assistant composes the title, the kind and the description, the current workspace's summary is attached; Send or Cmd+Enter files it into the monitor's own Linear project and shows the issue link.
|
|
211
213
|
- The gear at the bottom of the rail opens the settings sheet: the workspace list on the left, one workspace's form on the right (id, name, key pattern, theme colours, connector entries with the fields each connector declares (a boolean field is a checkbox showing its default when unset), rule texts and status words). Save writes the config file through `/api/config`; a token typed into a secret field goes to `/api/secrets` first and never into the file.
|
|
@@ -232,7 +234,8 @@ The queue is for agents that pull on their own: `POST /api/inbox` hands an item
|
|
|
232
234
|
|
|
233
235
|
- `npm install` once: it points `core.hooksPath` at `.githooks/`.
|
|
234
236
|
- The plugin's own code (skill, command, hooks, MCP server) is in BladeForge, `plugins/whose-turn`; a change to what Claude can do lands here as an API change first (ADR 0007).
|
|
235
|
-
- Releasing: bump `version` in `package.json`, add the changelog entry, commit, tag `v<version>` and push the tag; `.github/workflows/release.yml` checks, builds, publishes to npm
|
|
237
|
+
- Releasing: bump `version` in `package.json`, add the changelog entry, commit, tag `v<version>` and push the tag; `.github/workflows/release.yml` checks, builds, publishes to npm under `latest` on every tag, prereleases included, plus the channel tag such as `alpha` for a prerelease (npm trusts the workflow through OIDC, no token) and attaches the tarball to a GitHub Release.
|
|
238
|
+
- A checkout never offers the in-app update. To exercise it, lay out a packed copy as `<scratch>/app/node_modules/whose-turn`, start it with `WHOSE_TURN_DATA=<scratch>` and a free `WHOSE_TURN_PORT`, and point two test hooks at stubs: `WHOSE_TURN_REGISTRY` replaces `https://registry.npmjs.org` and `WHOSE_TURN_NPM` names the npm executable `POST /api/update` runs.
|
|
236
239
|
- `npm run dev` starts the server on port 8766 with data and config in `.users-files/dev/` (gitignored), so nothing touches the real config. It serves the last `npm run build`; for live reloading of the window run `npm run dev:web` next to it and open port 5173, which proxies the API to 8766.
|
|
237
240
|
- A fresh checkout has no window until `npm run build`; the server then says so on `/` while the API keeps working. A released package ships `dist/` built (RMA-126).
|
|
238
241
|
- `npm test` runs the suite (Node strips the types of the `web/src/lib/*.ts` modules it imports). `npm run test:web` runs the window's store and component tests on Vitest. `npm run check` runs `vue-tsc` and `tsc` over the server, ESLint over the window and the server, Stylelint and both suites (CI runs it on every pull request and push to `main`), `npm run format` runs Prettier over `web/src`; with `python3 scripts/docs-check.py` (the docs gate) and `/review` (the review gate) it runs on push.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const E=e=>fetch(e).then(t=>t.json(),()=>Promise.reject(new Error("unreachable"))),T=(e,t)=>fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t||{})}).then(n=>n.json(),()=>Promise.reject(new Error("unreachable"))),r=(e,t)=>(e.details||[]).find(n=>n["@type"]===t),S=e=>{var o,s;const t=r(e,"BadRequest");if(t&&t.field_violations)return t.field_violations.map(i=>i.description);const n=(s=(o=r(e,"ErrorInfo"))==null?void 0:o.metadata)==null?void 0:s.detail;return[n?String(n):e.message]},h=e=>{var t;return((t=r(e,"ErrorInfo"))==null?void 0:t.reason)||""},O=e=>fetch("/api/handoff?"+new URLSearchParams(e)).then(t=>t.ok?t.text():Promise.reject(new Error(String(t.status)))),_=e=>typeof e=="string"&&/^https:\/\//.test(e)?e:"",f="No workspace configured. Open Settings.",A=e=>e!=="",N=(e,t,n)=>e.some(o=>o.id===t&&o.open_moves!==n)?e.map(o=>o.id===t?{...o,open_moves:n}:o):e,y=(e,t)=>{const n=e.filter(a=>a.group==="you").map(a=>({row:a,handled:!!a.handled})).sort((a,d)=>a.row.rank-d.row.rank||String(a.row.updated).localeCompare(String(d.row.updated))),o=n.filter(a=>!a.handled).length,s=t==="handled";return{rows:n.filter(a=>a.handled===s),open:o,handled:n.length-o}},c=e=>String(e.status_since||e.updated),R=e=>e.filter(t=>t.group==="done").sort((t,n)=>c(n).localeCompare(c(t))).slice(0,15),P=e=>e.filter(t=>t.group==="others").sort((t,n)=>String(n.updated).localeCompare(String(t.updated))),D=e=>e==="failing"?"bad":e==="running"?"warn":e==="passing"?"ok":"",g={code_review:"info",qa:"info",on_hold:"warn"},b=e=>g[e||""]||"",v=e=>({open:"info",done:"ok",dropped:"warn"})[e||""]||"info",U=(e,t,n=1)=>e.filter(o=>o>t+n).length,p="unreachable",l="The local server is not answering",u=l+"; try again once it is back.",k=l+"; your text is kept.",m={NO_WORKSPACE:f,NOT_CONFIGURED:"The maintainer's tracker is not connected on this machine, so nothing can be sent from here.",UPSTREAM_FAILED:"The tracker did not answer; try again in a minute.",RATE_LIMITED:"The source is rate-limited; the monitor backs off.",OPEN_TOO_SOON:"The app window is already opening; give it a few seconds.",UPDATE_IN_PROGRESS:"An update is already installing; the monitor restarts when it is done.",UPDATE_UNAVAILABLE:"There is no update to install right now.",INTERNAL:"The local server failed; see its log."},w=(e,t="Not saved")=>m[h(e)]||t+": "+(e.message||"the server refused the change."),C=(e,{refused:t="Not saved",unreachable:n=u}={})=>{if(e instanceof Error)return e.message===p?n:t+": "+e.message;if(!e||typeof e!="object"||Array.isArray(e))return"";const o=e.error;return o?w(o,t):""},L=e=>{const t=Math.round((Date.now()-new Date(e).getTime())/6e4);return isFinite(t)?t<1?"now":t<60?t+"m ago":t<2880?Math.round(t/60)+"h ago":new Date(e).toLocaleDateString("en-US",{month:"short",day:"numeric"}):""},I=e=>{const t=(Date.now()-new Date(e??"").getTime())/36e5;return!isFinite(t)||t<0?{text:"",old:!1,hours:0}:{text:t<1?"<1h":t<48?Math.floor(t)+"h":Math.floor(t/24)+"d",old:t>=24,hours:t}},j=e=>e?new Date(e).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"}):"",M=(e,t)=>t?`${e}--${t}`:"";export{l as S,k as T,A as a,C as b,L as c,D as d,w as e,b as f,E as g,O as h,j as i,R as j,I as k,U as l,M as m,P as n,v as o,T as p,S as q,h as r,_ as s,N as w,y};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{w as X,t as Ye,r as Le,s as Jt,c as g,d as U,o as r,a as i,F as V,b as N,e as K,u as p,n as W,f as o,g as h,h as C,i as D,_ as j,j as F,k as O,l as q,m as Ue,p as mt,q as Ne,v as te,x as Zt,y as He,z as je,A as Xe,B as Qt,C as kt,D as es,E as ts,G as ss,H as ns}from"./_plugin-vue_export-helper-DHr5RHTx.js";import{s as os,m as yt,u as gt,a as bt,r as wt,p as as,b as ls,i as ot,l as $t,c as rs,M as G,d as be,e as ke,f as is,g as Ke,h as Me,j as cs,k as Ae,n as ds,o as us,q as ps,t as _s,v as at,w as hs}from"./scheme-DJ3WNw60.js";import{g as he,r as fs,a as vs,e as ms,w as xt,p as oe,b as le,T as Pe,S as $e,c as ae,h as ks,s as Ge,m as fe,d as ys,f as Ct,i as Je,j as gs,k as Ze,l as bs,y as ws,n as $s,o as xs,q as St}from"./bem-COKTjsky.js";const pe=os(wt),lt=(e,t)=>{const s=G,a=new Float32Array(s*s);for(let l=0;l<a.length;l+=1)a[l]=e[l]===t?1e6:0;const n=(l,c,u)=>{a[c]+u<a[l]&&(a[l]=a[c]+u)};for(let l=0;l<s;l+=1)for(let c=0;c<s;c+=1){const u=l*s+c;c>0&&n(u,u-1,1),l>0&&n(u,u-s,1),c>0&&l>0&&n(u,u-s-1,Math.SQRT2),c<s-1&&l>0&&n(u,u-s+1,Math.SQRT2)}for(let l=s-1;l>=0;l-=1)for(let c=s-1;c>=0;c-=1){const u=l*s+c;c<s-1&&n(u,u+1,1),l<s-1&&n(u,u+s,1),c<s-1&&l<s-1&&n(u,u+s+1,Math.SQRT2),c>0&&l<s-1&&n(u,u+s-1,Math.SQRT2)}return a},Ie=e=>{const t=String(e||"").trim(),s=/^#([0-9a-f]{6})$/i.exec(t);if(s)return[0,2,4].map(n=>parseInt(s[1].slice(n,n+2),16));const a=t.match(/[\d.]+/g);return a&&a.length>=3?a.slice(0,3).map(Number):null},Cs=e=>{const t=getComputedStyle(e),s=Ie(t.getPropertyValue("--md-primary"))||[44,86,151],a=Ie(t.getPropertyValue("--md-on-primary-container"))||s;return{core:ot()?s:s.map((n,l)=>$t(n,a[l],.35)),rim:Ie(t.getPropertyValue("--blob-3"))||[23,223,220],strength:ot()?.9:1}},Ss=(e,t,s)=>({size:e.range(.85,1.05)*Math.min(t,s),right:e.chance(.4),bottom:e.chance(.7),overflowX:e.range(-.02,.06),overflowY:e.range(-.02,.04)}),Ts=e=>{const t={minX:G,maxX:0,minY:G,maxY:0};for(let a=0;a<e.length;a+=1)if(e[a]){const n=a%G,l=Math.floor(a/G);t.minX=Math.min(t.minX,n),t.maxX=Math.max(t.maxX,n),t.minY=Math.min(t.minY,l),t.maxY=Math.max(t.maxY,l)}const s=a=>(a-G/2)/be;return{minX:s(t.minX),maxX:s(t.maxX),minY:s(t.minY),maxY:s(t.maxY)}},Ms=(e,t,s,a)=>{const n=t.maxX-t.minX,l=t.maxY-t.minY,c=Math.min(e.size,2*s*.95/n,2*a*.98/l),u=c/2,v=-e.overflowX*c,_=-e.overflowY*c;return{size:c,centerX:e.right?s-v-t.maxX*u:v-t.minX*u,centerY:e.bottom?a-_-t.maxY*u:_-t.minY*u}},As=(e,t,s)=>"rgba("+e.core.map((n,l)=>Math.round($t(n,e.rim[l],t))).join(",")+","+s.toFixed(3)+")",Rs=(e,t,s)=>{const a=[],n=t.size/62,l=t.size/2/be,c=t.size*is/4,u=n*.866;for(let v=0,_=t.centerY-c;_<t.centerY+c;v+=1,_+=u)for(let y=t.centerX-c+v%2*n/2;y<t.centerX+c;y+=n){const A=y+e.range(-.3,.3)*n,E=_+e.range(-.3,.3)*n,R=Math.floor((A-t.centerX)/l+G/2),S=Math.floor((E-t.centerY)/l+G/2);if(R<0||S<0||R>=G||S>=G)continue;const d=S*G+R,k=e.next(),f=e.range(.75,1.25),B=e.range(-.15,.15);if(s.inside[d]){const z=s.depth[d]/be,L=ke(z/.035),J=ke(z/.3);if(k>.5+.5*L)continue;a.push({x:A,y:E,radius:n*.5*(.45+.55*L)*(.95+.3*J)*f,alpha:.5+.25*L+.25*J,tint:ke(.65-.65*J+B)})}else{const z=s.gap[d]/be;if(k>.14*Math.exp(-z/.05)+.004)continue;a.push({x:A,y:E,radius:n*e.range(.1,.24),alpha:.35,tint:ke(.8+B)})}}return a},Es=()=>document.getElementById("wt"),Os=()=>document.getElementById("backdrop"),Tt=()=>Math.floor(Math.random()*4294967296),ye={},I={workspace:"",seed:Tt(),species:"",wantedSpecies:"",wantedPose:"",built:null},Ds=(e,t)=>{const s=rs(I.seed),a=pe[Math.floor(s.next()*pe.length)];I.species=pe.includes(I.wantedSpecies)?I.wantedSpecies:a;const n=Ss(s,e,t),l={facing:n.right?-1:1,tilt:s.range(-.07,.07),stretchX:s.range(.92,1.08),stretchY:s.range(.92,1.08)};s.chance(.25)&&(l.facing=-l.facing);const c=as(ls(wt,I.species,s,{pose:I.wantedPose}).shapes,l),u={inside:c,depth:lt(c,1),gap:lt(c,0)},v=Ms(n,Ts(c),e,t);return{key:I.seed+":"+I.species+":"+e+"x"+t,dots:Rs(s,v,u)}},xe=()=>{const e=Os();if(!e)return;const t=e.clientWidth,s=e.clientHeight,a=window.devicePixelRatio||1,n=Math.round(t*a),l=Math.round(s*a);(e.width!==n||e.height!==l)&&(e.width=n,e.height=l);const c=e.getContext("2d");if(!c||(c.setTransform(a,0,0,a,0,0),c.clearRect(0,0,t,s),!t||!s))return;const u=I.seed+":"+(I.wantedSpecies||"?")+":"+t+"x"+s;(!I.built||I.built.key.split(":")[0]!==String(I.seed)||!I.built.key.endsWith(t+"x"+s)||I.wantedSpecies&&I.species!==I.wantedSpecies)&&(I.built=Ds(t,s));const v=Es();if(!v)return u;const _=Cs(v);return I.built.dots.forEach(y=>{c.fillStyle=As(_,y.tint,y.alpha*_.strength),c.beginPath(),c.arc(y.x,y.y,y.radius,0,Math.PI*2),c.fill()}),u};window.whoseTurnBackdrop={species:pe.slice(),seeded:yt,get current(){return{workspace:I.workspace,species:I.species,seed:I.seed}},show:e=>{const t=e||{},s=String(t.workspace||"");ye[s]||(ye[s]=Tt());const a=t.seed!==void 0?Number(t.seed)>>>0:ye[s];ye[s]=a;const n=t.species&&pe.includes(t.species)?t.species:"",l=String(t.pose||"");return s===I.workspace&&a===I.seed&&n===I.wantedSpecies&&l===I.wantedPose&&I.built||(I.workspace=s,I.seed=a,I.wantedSpecies=n,I.wantedPose=l,I.built=null,xe()),window.whoseTurnBackdrop.current},repaint:xe};X(Ke,xe);gt(window,"resize",bt(xe,200));const{show:Mt,repaint:Is,seeded:Yr}=window.whoseTurnBackdrop,Bs=()=>{window.tippy&&window.tippy.delegate(document.body,{target:"[data-tip]",content:e=>e.dataset.tip||"",trigger:"mouseenter focus click",delay:[0,80],duration:[120,80],interactive:!0,maxWidth:440,placement:"top",theme:"wt",appendTo:()=>document.body})},Vs=()=>{let e=0;return{next:()=>++e,accepts:t=>t===e}},rt=e=>{const t=structuredClone(Ye(e)),s=t.connectors||{},a=t.rules||{};return a.statuses=a.statuses||{},{...t,connectors:Array.isArray(s)?s:Object.entries(s).map(([n,l])=>({type:n,...l})),rules:a,theme:t.theme||{}}},Ws=e=>{const t=structuredClone(Ye(e));return t.theme&&!Object.keys(t.theme).length&&delete t.theme,t.rules&&!Object.keys(t.rules.statuses||{}).length&&delete t.rules.statuses,t.rules&&!Object.keys(t.rules).length&&delete t.rules,t},it=new WeakMap;let Us=0;const ct=e=>{const t=Ye(e);let s=it.get(t);return s||(s=++Us,it.set(t,s)),s},dt=e=>JSON.stringify(e),Ns=(e,t)=>{const s=[];return e.forEach(a=>a.connectors.forEach(n=>{const l=t.get(n)||{};Object.keys(l).forEach(c=>{l[c]&&s.push({ws:a.id,connector:n.id||n.type,key:c,value:l[c]})})})),s},re=(e,t,s)=>{typeof s=="boolean"||s&&s.length?e[t]=s:delete e[t]},js=(e,t)=>{const s=a=>{const n=t.indexOf(a.id);return n<0?t.length:n};return[...e].sort((a,n)=>s(a)-s(n))},Ps=(e,t,s,a)=>{const n=e.filter(l=>l!==t);return n.splice(n.indexOf(s)+(a?0:1),0,t),n},Fs={gt:">",gte:"≥",lt:"<",lte:"≤",prefix:"starts with"},zs=(e,t)=>`${e} is ${t?"present":"absent"}`,qs=(e,t)=>Array.isArray(t)?`${e} in ${t.join(", ")}`:t&&typeof t=="object"?Object.entries(t).map(([s,a])=>s==="present"?zs(e,a):`${e} ${Fs[s]} ${JSON.stringify(a)}`).join(" and "):`${e} = ${t}`,Ys=e=>({when:Object.entries(e.when).map(([t,s])=>qs(t,s)).join(" and ")||"anything else",then:e.then.whose==="hidden"?"hidden":`${e.then.whose}: ${e.then.next}`}),Ls=e=>e.modal?"modal":e.settings?"settings":e.feedback?"feedback":null,Hs=(e,t)=>{const s=new URLSearchParams(e).get("mode");return s==="app"||s==="tab"?s:t==="app"?"app":"tab"},Xs=e=>"/?mode=tab"+(e?"&ws="+encodeURIComponent(e):""),Ks=e=>({width:e.outerWidth,height:e.outerHeight}),At={writeDefaults:!1},Rt=Me("wt-ws","",At),Et=cs("wt-mode","",At),m=Jt({workspaces:[],workspace:{id:"",name:"",connectors:[],links:[],key_pattern:"",theme:null},turn:[],feed:[],marks:{seen:[],base:0,handled:{}},counts:{mine:0,others:0,failing:0,done:0},todos:[],errors:{},health:{ok:!0,stale:!1,fresh_at:"",failing:[],alerts:[]},problems:[],polled_at:"",update:{current:"",latest:"",available:!1,state:"idle"}}),b=Le({ws:new URLSearchParams(location.search).get("ws")||Rt.value,open:new Set,mode:Hs(location.search,Et.value),live:!1,reconnects:0,status:"Connecting…",modal:null,feedback:!1,settings:!1,settingsDirty:!1,settingsAsk:!1,settingsAt:""});Et.value=b.mode;const Ce=Ae("",6e3),se=(e,t)=>{const s=le(e,t);return s&&(Ce.value=s),!s},Gs=()=>{b.mode==="app"&>(window,"resize",bt(()=>oe("/api/window",Ks(window)).catch(se),600))},Ot={resume:()=>{}},ce=us({scheduler:e=>{const t=ps(e,3e4,{immediate:!1});return Ot.resume=t.resume,t}}),ut=g(()=>{const e=m.workspace.theme;return e&&e.light&&e.dark?e[Ke.value?"dark":"light"]:{}}),H=g(()=>m.counts),pt=g(()=>[...m.problems||[],...m.health.alerts.map(e=>e.text),...Object.entries(m.errors||{}).map(([e,t])=>e+": "+t)]),Js=(e="")=>{b.settingsAt=e,b.settings=!0},Dt=g(()=>Ls(b)),Zs=()=>{const e=Dt.value;if(e==="modal"&&(b.modal=null),e==="feedback"&&(b.feedback=!1),e==="settings"){if(b.settingsDirty){b.settingsAsk=!0;return}b.settings=!1}},Fe=new Map,Qs=(e,t)=>{t&&Fe.set(e,document.activeElement)},en=(e,t)=>{if(t)return;const s=Fe.get(e);Fe.delete(e),s instanceof HTMLElement&&s.focus()},tn={modal:()=>!!b.modal,settings:()=>b.settings,feedback:()=>b.feedback};Object.entries(tn).forEach(([e,t])=>{X(t,s=>Qs(e,s),{flush:"sync"}),X(t,s=>en(e,s),{flush:"post"})});let Se=-1,ze=0;const _t=Vs(),ve=(e=b.ws)=>{const t=_t.next();return he("/api/state"+(e?"?ws="+encodeURIComponent(e):"")).then(s=>_t.accepts(t)?s.error?fs(s.error)!=="NO_SUCH_WORKSPACE"||!vs(e)?(b.status=ms(s.error,"Could not load"),m):ve(""):(Object.assign(m,s),b.ws=m.workspace.id,b.status="",Mt({workspace:m.workspace.id}),document.title="Whose Turn · "+m.workspace.name,m):m)},It=()=>(Se=-1,ve()),_e=ds("/api/stream",[],{immediate:!1,autoConnect:!1,autoReconnect:!0,serializer:{read:e=>JSON.parse(e||"null")}});X(_e.status,e=>{b.live=e==="OPEN",b.live&&(b.status="",b.reconnects++)},{flush:"sync"});X(_e.error,e=>{e&&(b.status="Disconnected from the local server, retrying…")},{flush:"sync"});X(_e.data,e=>{if(e){if("update"in e){m.update=e.update;return}typeof e.open_moves=="number"&&(m.workspaces=xt(m.workspaces,e.ws,e.open_moves)),!(m.workspace.id&&e.ws!==m.workspace.id)&&(b.status="",e.version!==Se?(Se=e.version,ze===0&&ve()):m.polled_at=e.polled_at)}},{flush:"sync"});const sn=()=>{_e.eventSource.value||_e.open()},nn=()=>(Ot.resume(),ve().then(sn)),Bt=e=>e===m.workspace.id?Promise.resolve():(Se=-1,ze++,ve(e).then(()=>{b.ws===e&&(Rt.value=b.ws,b.open.clear())},()=>{Ce.value=$e+"; staying on "+m.workspace.name+"."}).finally(()=>{ze--})),Vt=(e,t)=>oe(e,{ws:m.workspace.id,...t}),qe=e=>Vt("/api/marks",e).then(t=>{const s=le(t);return s||(Object.assign(m,{marks:t.marks,turn:t.turn,counts:t.counts}),m.workspaces=xt(m.workspaces,m.workspace.id,t.counts.mine)),s},t=>le(t)),on=e=>Vt("/api/todos",e).then(t=>{const s=le(t,{unreachable:Pe});return!s&&Array.isArray(t)&&(m.todos=t),s},t=>le(t,{unreachable:Pe})),an=e=>he("/api/config").then(t=>oe("/api/config",{workspaces:js(t.workspaces,e),secrets:[]})).then(t=>{se(t,{refused:"The order was not saved"})&&It()},t=>se(t,{refused:"The order was not saved"})),Be={id:"demo",name:"Demo",key_pattern:"\\bNW-\\d+\\b",theme:{seed:"#3d7a5c",accent:"#c27a2c"},connectors:[{type:"demo",scenario:"northwind",label:"Demo tracker"}]},ln=()=>he("/api/config").then(e=>e.workspaces.some(t=>t.id===Be.id)?null:oe("/api/config",{workspaces:[...e.workspaces,Be],secrets:[]})).then(e=>{if(se(e,{refused:"The demo workspace was not added"}))return Bt(Be.id)},e=>se(e,{refused:"The demo workspace was not added"})),ht=()=>{b.status="Checking now…",oe("/api/refresh").catch(e=>{b.status="",se(e)}),Mt({workspace:m.workspace.id,seed:Math.floor(Math.random()*4294967296)})},rn="M12 2.5l1.9 5.6 5.6 1.9-5.6 1.9L12 17.5l-1.9-5.6L4.5 10l5.6-1.9L12 2.5zM5 15l.9 2.6 2.6.9-2.6.9L5 22l-.9-2.6-2.6-.9 2.6-.9L5 15zm14-2l.7 2 2 .7-2 .7-.7 2-.7-2-2-.7 2-.7.7-2z",cn="M19.4 13a7.6 7.6 0 0 0 0-2l2-1.5-2-3.4-2.4 1a7.5 7.5 0 0 0-1.7-1L15 3.5H9l-.3 2.6a7.5 7.5 0 0 0-1.7 1l-2.4-1-2 3.4 2 1.5a7.6 7.6 0 0 0 0 2l-2 1.5 2 3.4 2.4-1a7.5 7.5 0 0 0 1.7 1l.3 2.6h6l.3-2.6a7.5 7.5 0 0 0 1.7-1l2.4 1 2-3.4-2-1.5zM12 15.5a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7z",dn="M19 8h-1.8a5.9 5.9 0 0 0-1.3-1.9l1.6-1.6-1.4-1.4-1.9 1.9A5.7 5.7 0 0 0 12 4.5a5.7 5.7 0 0 0-2.2.5L7.9 3.1 6.5 4.5l1.6 1.6A5.9 5.9 0 0 0 6.8 8H5v2h1.2a7 7 0 0 0 0 1v1H5v2h1.3a6 6 0 0 0 11.4 0H19v-2h-1.2v-1a7 7 0 0 0 0-1H19V8zm-7 10a4 4 0 0 1-4-4v-3a4 4 0 0 1 8 0v3a4 4 0 0 1-4 4zm-1-7h2v5h-2z",un=e=>{let t=2166136261;for(let s=0;s<e.length;s+=1)t^=e.charCodeAt(s),t=Math.imul(t,16777619);return t>>>0},Wt=e=>{const t=String(e||"").trim().split(/[\s_-]+/).filter(Boolean);return(t.length>=2?t.slice(0,3).map(a=>a.charAt(0)):(t[0]||"?").slice(0,2).split("")).join("").toUpperCase()},pn=e=>({1:17,2:14,3:11})[e.length]||11,_n=(e,t)=>{const s=t(un(e.id)),a=[],n=3+Math.floor(s()*3);for(let c=0;c<n;c+=1){const u=s()*Math.PI*2,v=9+s()*7;a.push({cx:(18+Math.cos(u)*v).toFixed(1),cy:(18+Math.sin(u)*v).toFixed(1),r:(1.6+s()*2.2).toFixed(1),opacity:(.35+s()*.4).toFixed(2)})}const l=Wt(e.name||e.id);return{dots:a,glyph:l,fontSize:pn(l)}},hn={class:"rail","aria-label":"Workspaces"},fn=["data-drop","href","aria-label","aria-selected","onClick","onDragstart","onDragover","onDrop"],vn={viewBox:"0 0 36 36",class:"rail__icon","aria-hidden":"true"},mn=["fill"],kn=["cx","cy","r","fill","opacity"],yn=["y","font-size","fill"],gn={class:"rail__label"},bn={key:0,class:"rail__badge"},wn={viewBox:"0 0 24 24",class:"rail__gear","aria-hidden":"true"},$n=["d"],xn=U({__name:"Rail",emits:["settings"],setup(e,{emit:t}){const s=t,a=g(()=>Object.fromEntries(m.workspaces.map(d=>[d.id,_n(d,yt)]))),n=g(()=>Object.fromEntries(m.workspaces.map(d=>{const k=d.theme?d.theme[Ke.value?"dark":"light"]:null;return[d.id,{fill:k?k["--md-primary-container"]:"var(--md-primary-container)",ink:k?k["--md-on-primary-container"]:"var(--md-on-primary-container)",dot:k?k["--md-primary"]:"var(--md-primary)"}]}))),l=d=>{d.detail>0&&d.currentTarget.blur()},c=(d,k)=>{l(d),Bt(k)},u=d=>{s("settings"),l(d)},v=D(""),_=D({}),y=d=>{const k=d.currentTarget;return d.clientY-k.getBoundingClientRect().top<k.offsetHeight/2},A=(d,k)=>{v.value=k.id,d.dataTransfer&&(d.dataTransfer.effectAllowed="move",d.dataTransfer.setData("text/plain",k.id))},E=()=>{v.value="",_.value={}},R=(d,k)=>{if(!v.value||v.value===k.id)return;d.preventDefault(),d.dataTransfer&&(d.dataTransfer.dropEffect="move");const f=y(d)?"before":"after";(_.value.id!==k.id||_.value.side!==f)&&(_.value={id:k.id,side:f})},S=(d,k)=>{if(!v.value||v.value===k.id)return;d.preventDefault();const f=Ps(m.workspaces.map(B=>B.id),v.value,k.id,y(d));_.value={},an(f)};return(d,k)=>(r(),i("nav",hn,[(r(!0),i(V,null,N(p(m).workspaces,f=>(r(),i("a",{key:f.id,class:W(["rail__item",{"rail__item--dragging":v.value===f.id}]),"data-drop":_.value.id===f.id?_.value.side:null,href:"?ws="+encodeURIComponent(f.id),"aria-label":f.name,"aria-selected":f.id===p(m).workspace.id,draggable:"true",onClick:K(B=>c(B,f.id),["prevent"]),onDragstart:B=>A(B,f),onDragend:E,onDragover:B=>R(B,f),onDragleave:k[0]||(k[0]=B=>_.value={}),onDrop:B=>S(B,f)},[(r(),i("svg",vn,[o("rect",{x:"0",y:"0",width:"36",height:"36",rx:"10",fill:n.value[f.id].fill},null,8,mn),(r(!0),i(V,null,N(a.value[f.id].dots,(B,z)=>(r(),i("circle",{key:z,cx:B.cx,cy:B.cy,r:B.r,fill:n.value[f.id].dot,opacity:B.opacity},null,8,kn))),128)),o("text",{x:"18",y:a.value[f.id].fontSize>=17?24:23,"text-anchor":"middle","font-size":a.value[f.id].fontSize,"font-weight":"700","letter-spacing":"-0.5","font-family":"Roboto, Inter, system-ui, sans-serif",fill:n.value[f.id].ink},h(a.value[f.id].glyph),9,yn)])),o("span",gn,h(f.name),1),f.open_moves?(r(),i("span",bn,h(f.open_moves),1)):C("",!0)],42,fn))),128)),k[2]||(k[2]=o("span",{class:"rail__spacer"},null,-1)),o("a",{class:"rail__item",href:"#settings","aria-label":"Settings",onClick:K(u,["prevent"])},[(r(),i("svg",wn,[o("path",{d:p(cn),fill:"currentColor"},null,8,$n)])),k[1]||(k[1]=o("span",{class:"rail__label"},"Settings",-1))])]))}}),Cn=j(xn,[["__scopeId","data-v-be863916"]]),Ve=" · checks every minute",Ut=(e,t,s,a)=>{const n=e.failing.map(c=>c.source),l=t?ae(t):"—";return n.length&&n.length>=s?{dot:"off",text:"No data: "+n.join(", ")+" failing · last data "+ae(e.fresh_at),failing:n,noData:!0}:e.stale?{dot:"off",text:"Stale · last data "+ae(e.fresh_at)+Ve,failing:n,noData:!1}:n.length?{dot:a?"warn":"off",text:n.join(", ")+" failing · last check "+l+Ve,failing:n,noData:!1}:{dot:a?"ok":"off",text:"Live · last check "+l+Ve,failing:n,noData:!1}},Sn=["data-tip"],Tn={class:"ai-button__icon",viewBox:"0 0 24 24","aria-hidden":"true"},Mn=["d"],An=U({__name:"AiButton",props:{query:{},big:{type:Boolean}},setup(e){const t=e,s={"":"Copy for AI: this with its context as text, ready to paste into Claude or any other assistant",sent:"Copied",failed:"Could not copy"},a=Ae("",1600),n=g(()=>s[a.value]),l=()=>ks(t.query).then(c=>navigator.clipboard.writeText(c)).then(()=>{a.value="sent"},()=>{a.value="failed"});return(c,u)=>(r(),i("button",{type:"button",class:W(["ai-button",{"ai-button--big":e.big,"ai-button--sent":p(a)==="sent","ai-button--failed":p(a)==="failed"}]),"aria-label":"Copy for AI","data-tip":n.value,onClick:K(l,["stop"])},[(r(),i("svg",Tn,[o("path",{d:p(rn),fill:"currentColor"},null,8,Mn)]))],10,Sn))}}),de=j(An,[["__scopeId","data-v-d32a60c7"]]),Rn={key:0,class:"update-chip update-chip--done",role:"status"},En={key:1,class:"update-chip update-chip--busy",role:"status"},On=["title"],Dn=15e3,In=U({__name:"UpdateChip",setup(e){const t=D(!1),s=D(""),a=Ae("",Dn),n=g(()=>m.update),l=g(()=>t.value||n.value.state==="installing"||n.value.state==="restarting");X(()=>n.value.current,u=>{s.value&&u&&u!==s.value&&(a.value=u,s.value="")});const c=()=>{if(l.value)return;t.value=!0,s.value=n.value.current;const u=v=>{if(t.value=!1,!se(v,{refused:"The update did not start"})){s.value="";return}m.update=v.update};oe("/api/update").then(u,u)};return(u,v)=>p(a)?(r(),i("span",Rn,"Updated to "+h(p(a)),1)):l.value?(r(),i("span",En,"Updating…")):n.value.available&&n.value.state==="failed"?(r(),i("button",{key:2,type:"button",class:"update-chip update-chip--failed",title:n.value.reason,onClick:c}," Update failed · Retry ",8,On)):n.value.available?(r(),i("button",{key:3,type:"button",class:"update-chip",onClick:c},h(n.value.latest)+" is out · Update ",1)):C("",!0)}}),Bn=j(In,[["__scopeId","data-v-f70d2aed"]]),Vn={class:"status-bar"},Wn={class:"status-bar__icon",viewBox:"0 0 24 24","aria-hidden":"true"},Un=["d"],Nn={key:0,class:"status-bar__notice",role:"alert"},jn=["href"],Pn=["aria-disabled"],Fn=5e3,zn=U({__name:"StatusBar",emits:["report"],setup(e,{emit:t}){const s=t,a=g(()=>(ce.value,Ut(m.health,m.polled_at,m.workspace.connectors.length,b.live))),n=D(!1),l=D(!1),c=g(()=>l.value?"Opened in the app window. You can close this tab.":b.status||a.value.text),u=_=>se(_,{refused:"The app window did not open"}),v=()=>{if(n.value)return;n.value=!0,setTimeout(()=>n.value=!1,Fn);const _=y=>{u(y),l.value=!le(y)};oe("/api/open",{mode:"app"}).then(_,_)};return(_,y)=>(r(),i("div",Vn,[o("button",{type:"button",class:"btn status-bar__btn status-bar__report",title:"A bug or a wish about this monitor, straight to its backlog",onClick:y[0]||(y[0]=A=>s("report"))},[(r(),i("svg",Wn,[o("path",{d:p(dn),fill:"currentColor"},null,8,Un)])),y[2]||(y[2]=F("Report ",-1))]),o("span",{class:W(["status-bar__live",{"status-bar__live--off":a.value.dot==="off","status-bar__live--warn":a.value.dot==="warn"}])},null,2),o("span",null,h(c.value),1),p(Ce)?(r(),i("span",Nn,h(p(Ce)),1)):C("",!0),O(Bn),o("button",{type:"button",class:"btn status-bar__btn",onClick:y[1]||(y[1]=(...A)=>p(ht)&&p(ht)(...A))},"Refresh now"),p(b).mode==="app"?(r(),i("a",{key:1,class:"btn status-bar__btn",href:p(Xs)(p(b).ws),target:"_blank",rel:"noopener"},"Open in browser",8,jn)):(r(),i("button",{key:2,type:"button",class:W(["btn status-bar__btn",{"status-bar__btn--busy":n.value}]),"aria-disabled":n.value,onClick:v},h(n.value?"Opening…":"Open as app"),11,Pn)),O(de,{query:{all:"1"}}),y[3]||(y[3]=o("span",{class:"ai-hint status-bar__ai-hint"},"AI: copy everything",-1))]))}}),qn=j(zn,[["__scopeId","data-v-eefe1de8"]]),Nt=e=>{const t=Me("wt-collapsed",[],{writeDefaults:!1}),s=g(()=>t.value.includes(e));return{collapsed:s,toggle:()=>{t.value=s.value?t.value.filter(n=>n!==e):[...t.value,e]}}},Yn=["href"],Ln={key:1},ne=U({__name:"ExtLink",props:{text:{default:""},url:{default:""}},setup(e){const t=e,s=g(()=>Ge(t.url));return(a,n)=>s.value?(r(),i("a",{key:0,href:s.value,target:"_blank",rel:"noopener"},h(e.text),9,Yn)):(r(),i("span",Ln,h(e.text),1))}}),Hn={class:"workspace-header__title"},Xn={class:"workspace-header__name"},Kn={class:"workspace-header__line workspace-header__sentence"},Gn={class:"workspace-header__count"},Jn={class:"workspace-header__count"},Zn={key:0},Qn={class:"workspace-header__count"},eo={key:0,class:"workspace-header__line workspace-header__strip"},to={class:"workspace-header__count"},so={class:"workspace-header__count"},no={class:"workspace-header__count"},oo={class:"workspace-header__count"},ao={class:"workspace-header__line workspace-header__give"},lo={class:"workspace-header__links"},ro={class:"workspace-header__tiles"},io={class:"workspace-header__tile workspace-header__tile--you"},co={class:"workspace-header__tile-value"},uo={class:"workspace-header__tile"},po={class:"workspace-header__tile-value"},_o={class:"workspace-header__tile-value"},ho={class:"workspace-header__tile"},fo={class:"workspace-header__tile-value"},vo=U({__name:"WorkspaceHeader",setup(e){const{collapsed:t,toggle:s}=Nt("header");return(a,n)=>(r(),i("header",{id:"summary",class:W(["island workspace-header",{"workspace-header--collapsed":p(t)}])},[o("div",Hn,[o("h1",Xn,h(p(m).workspace.name||"Whose Turn"),1),o("p",Kn,[o("b",Gn,h(p(H).mine),1),n[3]||(n[3]=F(" waiting on you, ",-1)),o("b",Jn,h(p(H).others),1),n[4]||(n[4]=F(" waiting on someone else",-1)),p(H).failing?(r(),i("span",Zn,[n[1]||(n[1]=F(", ",-1)),o("b",Qn,h(p(H).failing),1),n[2]||(n[2]=F(" failing",-1))])):C("",!0),n[5]||(n[5]=F(". ",-1))]),p(t)?(r(),i("p",eo,[n[6]||(n[6]=F(" Your move ",-1)),o("b",to,h(p(H).mine),1),n[7]||(n[7]=F(" · Waiting on others ",-1)),o("b",so,h(p(H).others),1),n[8]||(n[8]=F(" · Failing ",-1)),o("b",no,h(p(H).failing),1),n[9]||(n[9]=F(" · Done ",-1)),o("b",oo,h(p(H).done),1)])):C("",!0),o("p",ao,[O(de,{query:{ws:p(m).workspace.id,kind:"workspace"},big:""},null,8,["query"]),n[10]||(n[10]=o("span",{class:"ai-hint"},"AI: copy this workspace",-1)),o("span",lo,[(r(!0),i(V,null,N(p(m).workspace.links||[],l=>(r(),q(ne,{key:l.url,text:l.label,url:l.url,title:l.url},null,8,["text","url","title"]))),128))])])]),o("button",{type:"button",class:W(["fold-toggle workspace-header__fold",{"fold-toggle--folded":p(t)}]),"aria-label":"Collapse or expand header","data-tip":"Fold the header; it stays folded until you open it again",onClick:n[0]||(n[0]=K((...l)=>p(s)&&p(s)(...l),["stop"]))}," ▾ ",2),o("div",ro,[o("div",io,[n[11]||(n[11]=o("span",{class:"workspace-header__tile-label"},"Your move",-1)),o("strong",co,h(p(H).mine),1)]),o("div",uo,[n[12]||(n[12]=o("span",{class:"workspace-header__tile-label"},"Waiting on others",-1)),o("strong",po,h(p(H).others),1)]),o("div",{class:W(["workspace-header__tile",{"workspace-header__tile--fail":p(H).failing}])},[n[13]||(n[13]=o("span",{class:"workspace-header__tile-label"},"Open PRs with failing checks",-1)),o("strong",_o,h(p(H).failing),1)],2),o("div",ho,[n[14]||(n[14]=o("span",{class:"workspace-header__tile-label"},"Done in the last 30 days",-1)),o("strong",fo,h(p(H).done),1)])])],2))}}),mo=j(vo,[["__scopeId","data-v-0f4b6d6e"]]),Qe=e=>{X(()=>b.reconnects,e)},ko=["id"],yo={class:"island__head"},go={class:"island__title"},bo={class:"island__note"},wo={class:"island__note-text"},$o=["data-tip"],xo=["aria-label"],Co={class:"island__body"},So=U({__name:"Island",props:{id:{},title:{},note:{default:""},tip:{default:""},column:{type:Boolean}},setup(e){const t=e,{collapsed:s,toggle:a}=Nt(t.id);return(n,l)=>(r(),i("section",{id:e.id,class:W(["island",{"island--collapsed":p(s),"island--column":e.column}])},[o("div",yo,[o("h2",go,[F(h(e.title)+" ",1),Ue(n.$slots,"head",{},void 0)]),o("p",bo,[o("span",wo,h(e.note),1),e.tip?(r(),i("button",{key:0,type:"button",class:"hint-icon","aria-label":"More info","data-tip":e.tip}," ? ",8,$o)):C("",!0),o("button",{type:"button",class:W(["fold-toggle",{"fold-toggle--folded":p(s)}]),"aria-label":"Collapse or expand "+e.id,"data-tip":"Fold this panel; it stays folded until you open it again",onClick:l[0]||(l[0]=K((...c)=>p(a)&&p(a)(...c),["stop"]))}," ▾ ",10,xo)])]),o("div",Co,[Ue(n.$slots,"default",{},void 0)])],10,ko))}}),Re=j(So,[["__scopeId","data-v-a03b7f62"]]),To=["aria-label"],Mo=["aria-pressed","onClick"],Ao={key:0,class:"segmented__count"},Ro=U({__name:"Segmented",props:Ne({options:{},label:{default:""},chips:{type:Boolean}},{modelValue:{required:!0},modelModifiers:{}}),emits:["update:modelValue"],setup(e){const t=mt(e,"modelValue");return(s,a)=>(r(),i("span",{class:W(["segmented",{"segmented--chips":e.chips}]),role:"group","aria-label":e.label},[(r(!0),i(V,null,N(e.options,n=>(r(),i("button",{key:n.id,type:"button",class:"segmented__option","aria-pressed":t.value===n.id,onClick:l=>t.value=n.id},[F(h(n.label),1),n.count!==void 0?(r(),i("b",Ao,h(n.count),1)):C("",!0)],8,Mo))),128))],10,To))}}),Te=j(Ro,[["__scopeId","data-v-7901b5d6"]]),Eo={class:"ticket-cell"},Oo={class:"ticket-cell__summary"},Do=U({__name:"TicketCell",props:{row:{}},setup(e){return(t,s)=>(r(),i("td",Eo,[O(ne,{text:e.row.key,url:e.row.ticket_url,class:"ticket-key"},null,8,["text","url"]),o("span",Oo,h(e.row.summary),1)]))}}),et=j(Do,[["__scopeId","data-v-8436de88"]]),Io={key:0,class:"pr-cell"},Bo={key:1,class:"pill"},Vo=U({__name:"PrCell",props:{row:{}},setup(e){return(t,s)=>(r(),i("td",null,[e.row.pr?(r(),i("span",Io,[O(ne,{text:e.row.pr,url:e.row.pr_url,class:"pr-cell__link"},null,8,["text","url"]),e.row.pr_state==="OPEN"&&e.row.checks?(r(),i("span",{key:0,class:W(["pill",p(fe)("pill",p(ys)(e.row.checks))])},h(e.row.checks),3)):e.row.pr_state?(r(),i("span",Bo,h(e.row.pr_state.toLowerCase()),1)):C("",!0)])):C("",!0)]))}}),jt=j(Vo,[["__scopeId","data-v-75c443c3"]]),Wo=["open"],Uo={class:"recently-done__summary"},No={class:"recently-done__count"},jo={class:"recently-done__list"},Po={class:"table"},Fo=["data-tip"],zo={class:"table__actions"},qo=U({__name:"RecentlyDone",setup(e){const t=Me("wt-recent-open",!1,{writeDefaults:!1,serializer:{read:n=>n==="1",write:n=>n?"1":"0"}}),s=n=>{t.value=n.target.open},a=g(()=>(ce.value,gs(m.turn).map(n=>({row:n,since:Ze(n.status_since||n.updated)}))));return(n,l)=>a.value.length?(r(),i("details",{key:0,class:"recently-done",open:p(t),onToggle:s},[o("summary",Uo,[l[0]||(l[0]=F(" Recently done ",-1)),o("span",No,h(a.value.length),1)]),o("div",jo,[o("table",Po,[l[1]||(l[1]=o("thead",null,[o("tr",null,[o("th",null,"Ticket"),o("th",null,"Status"),o("th",null,"Done"),o("th",{class:"table__actions-col"},[o("span",{class:"visually-hidden"},"Copy for AI")])])],-1)),o("tbody",null,[(r(!0),i(V,null,N(a.value,({row:c,since:u})=>(r(),i("tr",{key:c.row_id},[O(et,{row:c},null,8,["row"]),o("td",null,[o("span",{class:W(["pill",p(fe)("pill",p(Ct)(c.status_group))])},h(c.status),3)]),o("td",null,[o("span",{class:"age","data-tip":"Done "+p(Je)(c.status_since||c.updated)},h(u.text),9,Fo)]),o("td",zo,[O(de,{class:"table__ai",query:{ws:p(m).workspace.id,kind:"row",ref:c.row_id}},null,8,["query"])])]))),128))])])])],40,Wo)):C("",!0)}}),Yo=j(qo,[["__scopeId","data-v-12ee20e8"]]),Lo=13,Ho=U({__name:"RowScroll",props:{floor:{type:Boolean}},setup(e){const t=D(null),s=D(null),{y:a}=_s(t),{height:n}=at(t),{height:l}=at(s),c=()=>{var y,A,E;return[...((E=(A=(y=s.value)==null?void 0:y.querySelector("table"))==null?void 0:A.tBodies[0])==null?void 0:E.rows)||[]]},u=()=>{const y=t.value;return y?y.getBoundingClientRect().top+y.clientTop+y.clientHeight:0},v=g(()=>(a.value,n.value,l.value,bs(c().map(y=>y.getBoundingClientRect().bottom),u(),Lo))),_=()=>{var d,k;const y=t.value,A=c(),E=A[A.length-v.value];if(!y||!E)return;const S=((k=(d=s.value)==null?void 0:d.querySelector("thead"))==null?void 0:k.getBoundingClientRect().bottom)??y.getBoundingClientRect().top;y.scrollBy({top:E.getBoundingClientRect().top-S,behavior:"smooth"})};return(y,A)=>(r(),i(V,null,[o("div",{ref_key:"scroller",ref:t,class:W(["row-scroll",{"row-scroll--floor":e.floor}])},[o("div",{ref_key:"content",ref:s},[Ue(y.$slots,"default",{},void 0)],512)],2),v.value?(r(),i("button",{key:0,type:"button",class:"row-scroll__more",onClick:_}," +"+h(v.value)+" more ",1)):C("",!0)],64))}}),Pt=j(Ho,[["__scopeId","data-v-7bd9ace7"]]),Xo=D("open"),Ko=()=>Xo,Go={class:"table"},Jo={class:"hint-row"},Zo={class:"your-move__next"},Qo=["data-tip"],ea=["data-tip"],ta=["data-tip"],sa={key:0,class:"alert alert--inline your-move__problem",role:"alert"},na={class:"table__actions"},oa=["aria-label","onClick"],aa={key:0,class:"empty"},la=U({__name:"YourMove",setup(e){const t=Ko(),s=g(()=>{ce.value;const u=ws(m.turn,t.value);return{...u,rows:u.rows.map(v=>({...v,age:Ze(v.row.activity_at)}))}}),a=g(()=>[{id:"open",label:"Open",count:s.value.open},{id:"handled",label:"Handled",count:s.value.handled}]),n=g(()=>{if(t.value==="handled")return"Nothing marked as handled.";const{noData:u,failing:v}=Ut(m.health,m.polled_at,m.workspace.connectors.length,b.live);return u?"No data: "+v.join(", ")+(v.length===1?" is":" are")+" failing. Check Settings.":"Nothing is waiting on you."}),l=Le(new Map);Qe(()=>l.clear());const c=async u=>{const v=u.row.row_id,_=await qe({handled:{[v]:u.handled?null:new Date().toISOString()}});_?l.set(v,_):l.delete(v)};return(u,v)=>(r(),q(Re,{id:"your-move",title:"Your move",note:"Next step is yours",tip:"Each row is something where the next step is yours. Ticket, PR and comment open in a new tab."},{head:te(()=>[O(Te,{modelValue:p(t),"onUpdate:modelValue":v[0]||(v[0]=_=>Zt(t)?t.value=_:null),options:a.value,label:"Show",class:"your-move__view"},null,8,["modelValue","options"])]),default:te(()=>[O(Pt,{floor:""},{default:te(()=>[o("table",Go,[v[1]||(v[1]=o("thead",null,[o("tr",null,[o("th",null,"Ticket"),o("th",null,"Next step"),o("th",null,"PR"),o("th",{class:"hide-narrow"},"Last comment"),o("th",{class:"table__actions-col"},[o("span",{class:"visually-hidden"},"Mark")])])],-1)),o("tbody",null,[(r(!0),i(V,null,N(s.value.rows,_=>(r(),i("tr",{key:_.row.row_id,class:W(["your-move__row",{"your-move__row--handled":_.handled}])},[O(et,{row:_.row},null,8,["row"]),o("td",null,[o("span",Jo,[o("span",Zo,h(_.row.next),1),_.age.text?(r(),i("span",{key:0,class:W(["age",{"age--old":_.age.old}]),"data-tip":"Waiting since "+p(Je)(_.row.activity_at)},h(_.age.text),11,Qo)):C("",!0),_.row.why?(r(),i("button",{key:1,type:"button",class:"hint-icon","aria-label":"Why this is your move","data-tip":_.row.why}," ? ",8,ea)):C("",!0),_.handled?(r(),i("span",{key:2,class:"handled-chip","data-tip":"Marked "+p(ae)(p(m).marks.handled[_.row.row_id]||"")+". Comes back in 7 days if it is still open, or sooner when something new happens."},"handled",8,ta)):C("",!0)]),l.get(_.row.row_id)?(r(),i("p",sa,h(l.get(_.row.row_id)),1)):C("",!0)]),O(jt,{row:_.row},null,8,["row"]),o("td",{class:W(["hide-narrow",{"your-move__quote":_.row.comment}])},[_.row.comment?(r(),q(ne,{key:0,text:_.row.comment,url:_.row.comment_url},null,8,["text","url"])):C("",!0)],2),o("td",na,[O(de,{class:"table__ai",query:{ws:p(m).workspace.id,kind:"row",ref:_.row.row_id}},null,8,["query"]),o("button",{type:"button",class:W(["your-move__mark",{"your-move__mark--undo":_.handled}]),"aria-label":(_.handled?"Move back to open: ":"Mark as handled: ")+_.row.key,onClick:K(y=>c(_),["stop"])},h(_.handled?"Undo":"Handled"),11,oa)])],2))),128))])])]),_:1}),s.value.rows.length?C("",!0):(r(),i("p",aa,h(n.value),1)),p(t)==="open"?(r(),q(Yo,{key:1})):C("",!0)]),_:1}))}}),ra=j(la,[["__scopeId","data-v-e3e1502a"]]),ia=D(!1),ca=()=>ia,da={class:"todos__add"},ua={key:0,class:"alert alert--inline",role:"alert"},pa={class:"todos__list"},_a=["aria-checked","aria-label","onClick"],ha={class:"todos__text"},fa={class:"todos__meta"},va=["onClick"],ma={key:0,class:"empty todos__empty"},ka=U({__name:"Todos",setup(e){const t=ca(),s=D(""),a=g(()=>(m.todos||[]).filter(d=>!d.done)),n=g(()=>(m.todos||[]).filter(d=>d.done)),l=g(()=>new RegExp(m.workspace.key_pattern||"(?!)","g")),c=d=>{const k=[];let f=0;const B=d.text;for(const z of B.matchAll(l.value))z.index>f&&k.push({text:B.slice(f,z.index)}),k.push({text:z[0],url:z[0]===d.key?d.ticket_url:""}),f=z.index+z[0].length;return f<B.length&&k.push({text:B.slice(f)}),k},u=g(()=>(ce.value,(t.value?a.value.concat(n.value):a.value).map(d=>({todo:d,parts:c(d),meta:(d.by&&d.by!=="you"?d.by+" · ":"")+ae(d.created)})))),v=D("");Qe(()=>{v.value=""});const _=async d=>(v.value=await on(d),!v.value),y=async()=>{const d=s.value.trim();d&&await _({action:"add",text:d})&&s.value.trim()===d&&(s.value="")},A=d=>_({action:d.done?"undo":"done",id:d.id}),E=d=>_({action:"delete",id:d.id}),R=g({get:()=>t.value?"done":"open",set:d=>{t.value=d==="done"}}),S=g(()=>[{id:"open",label:"Open",count:a.value.length},{id:"done",label:"Done",count:n.value.length}]);return(d,k)=>(r(),q(Re,{id:"todo",title:"To do",note:"Your own notes",tip:'Your personal to-dos, kept on this machine. Add here, or ask Claude: "add to my to-do: …". A ticket key like PROJ-123 in the text becomes a link.'},{head:te(()=>[O(Te,{modelValue:R.value,"onUpdate:modelValue":k[0]||(k[0]=f=>R.value=f),options:S.value,label:"Show",class:"todos__view"},null,8,["modelValue","options"])]),default:te(()=>[o("div",da,[He(o("input",{"onUpdate:modelValue":k[1]||(k[1]=f=>s.value=f),class:"input todos__input",type:"text",placeholder:"Add a to-do and press Enter","aria-label":"New to-do",autocomplete:"off",onKeydown:je(y,["enter"])},null,544),[[Xe,s.value]]),o("button",{type:"button",class:"btn btn--tonal",onClick:y},"Add")]),v.value?(r(),i("p",ua,h(v.value),1)):C("",!0),o("ul",pa,[(r(!0),i(V,null,N(u.value,({todo:f,parts:B,meta:z})=>(r(),i("li",{key:f.id,class:W(["todos__item",{"todos__item--done":f.done}])},[o("button",{type:"button",class:W(["todos__check",{"todos__check--on":f.done}]),role:"checkbox","aria-checked":!!f.done,"aria-label":f.done?"Mark not done":"Mark done",onClick:L=>A(f)},null,10,_a),o("span",ha,[(r(!0),i(V,null,N(B,(L,J)=>(r(),i(V,{key:J},[L.url!==void 0?(r(),q(ne,{key:0,text:L.text,url:L.url},null,8,["text","url"])):(r(),i(V,{key:1},[F(h(L.text),1)],64))],64))),128))]),o("span",fa,h(z),1),o("button",{type:"button",class:"todos__delete","aria-label":"Delete to-do",onClick:L=>E(f)}," × ",8,va)],2))),128)),u.value.length?C("",!0):(r(),i("span",ma,h(p(t)?"Nothing here yet.":"Nothing to do. Add one above, or ask Claude."),1))])]),_:1}))}}),ya=j(ka,[["__scopeId","data-v-6f034e1e"]]),ga={key:0,class:"empty"},ba={class:"table waiting__table"},wa=["data-tip"],$a={class:"hide-narrow"},xa={key:0,class:"hint-row"},Ca=["data-tip"],Sa={class:"table__actions"},Ta=U({__name:"Waiting",setup(e){const t=g(()=>(ce.value,$s(m.turn).map(s=>({row:s,since:s.status_since?Ze(s.status_since):null}))));return(s,a)=>(r(),q(Re,{id:"waiting",title:"Waiting on others",note:"Someone else holds the next step",tip:"Tickets you touched in the last 30 days where someone else holds the next step: a reviewer, QA, or a new owner."},{default:te(()=>[t.value.length?(r(),q(Pt,{key:1},{default:te(()=>[o("table",ba,[a[0]||(a[0]=o("thead",null,[o("tr",null,[o("th",{class:"waiting__col waiting__col--ticket"},"Ticket"),o("th",{class:"waiting__col waiting__col--status"},"Status"),o("th",{class:"waiting__col waiting__col--with"},"With"),o("th",{class:"hide-narrow"},"Waiting for"),o("th",{class:"waiting__col waiting__col--pr"},"PR"),o("th",{class:"table__actions-col waiting__col waiting__col--actions"},[o("span",{class:"visually-hidden"},"Copy for AI")])])],-1)),o("tbody",null,[(r(!0),i(V,null,N(t.value,({row:n,since:l})=>(r(),i("tr",{key:n.row_id},[O(et,{row:n},null,8,["row"]),o("td",null,[o("span",{class:W(["pill",p(fe)("pill",p(Ct)(n.status_group))])},h(n.status),3)]),o("td",null,[F(h(n.whose)+" ",1),l?(r(),i("span",{key:0,class:W(["age",{"age--old":l.hours>=72}]),"data-tip":n.status+" since "+p(Je)(n.status_since)},h(l.text),11,wa)):C("",!0)]),o("td",$a,[n.hold_url?(r(),i("span",xa,[O(ne,{text:n.next,url:n.hold_url,class:"waiting__reason"},null,8,["text","url"]),o("button",{type:"button",class:"hint-icon","aria-label":"Full reason","data-tip":n.hold_full||n.next}," ? ",8,Ca)])):(r(),i(V,{key:1},[F(h(n.next),1)],64))]),O(jt,{row:n},null,8,["row"]),o("td",Sa,[O(de,{class:"table__ai",query:{ws:p(m).workspace.id,kind:"row",ref:n.row_id}},null,8,["query"])])]))),128))])])]),_:1})):(r(),i("p",ga,"Nothing is waiting on someone else."))]),_:1}))}}),Ma=j(Ta,[["__scopeId","data-v-a632447c"]]),Aa=(e,t)=>e.seen instanceof Set?e.seen.has(t):e.seen.includes(t),Ft=(e,t)=>new Date(e.at).getTime()>(t.base||0)&&!Aa(t,e.id),Ra=(e,t)=>{const s=String(t||"").toLowerCase().split(/\s+/).filter(Boolean);if(!s.length)return!0;const a=[e.ref,e.key,e.title,e.kind,e.who,e.text,e.body].join(" ").toLowerCase();return s.every(n=>a.includes(n))},Ea=e=>e.key||e.ref||e.id,Oa=(e,t)=>{const s=new Map;for(const a of e){const n=Ea(a),l=s.get(n)||{id:n,items:[],newest:a.at,unread:0,title:""};s.set(n,l),l.items.push(a),l.title||(l.title=a.title||""),a.at>l.newest&&(l.newest=a.at),Ft(a,t)&&(l.unread+=1)}return[...s.values()].sort((a,n)=>String(n.newest).localeCompare(String(a.newest)))},Da=e=>new Set(e.filter(t=>t.group==="you"&&!t.handled).map(t=>t.key)),Ia=(e,t=2)=>{const s=[...new Set(e.map(n=>n.kind))],a=s.length-t;return s.slice(0,t).join(" · ")+(a>0?` +${a}`:"")},Ba=e=>/fail|changes/.test(e)?"bad":/approved|merged|passed/.test(e)?"ok":"",Va=e=>/comment|approved|changes|mentioned|review requested/.test(e.kind)&&String(e.body||"").trim()!=="",zt=e=>e.ref!==e.key&&(e.key||e.title)||"",Wa=e=>[zt(e),e.text===e.title?"":e.text].filter(Boolean).join(" · "),Ua=e=>e.type==="github"&&e.label==="GitHub"?"GH":Wt(e.label),Na={class:"feed-item__body"},ja={class:"feed-item__top"},Pa={key:0,class:"feed-item__who"},Fa={key:1,class:"at-you"},za={class:"feed-item__right"},qa={class:"timestamp feed-item__when"},Ya=["aria-label","aria-expanded"],La={key:0,class:"feed-item__text"},Ha={class:"feed-item__full"},Xa={class:"feed-item__actions"},Ka={key:1,class:"feed-item__text"},Ga=U({__name:"FeedItem",props:{item:{},source:{},fresh:{type:Boolean},lit:{type:Boolean},expanded:{type:Boolean}},emits:["open","toggle","seen"],setup(e,{emit:t}){const s=e,a=t,n=g(()=>Ua(s.source)),l=g(()=>zt(s.item)),c=g(()=>Wa(s.item)),u=g(()=>Ba(s.item.kind)),v=g(()=>u.value||(s.source.type==="github"?"":"jira")),_=g(()=>Va(s.item)),y=g(()=>_.value&&s.expanded),A=g(()=>(ce.value,ae(s.item.at))),E=()=>{s.fresh&&a("seen",s.item.id)},R=()=>{a("toggle",s.item.id),E()};return(S,d)=>(r(),i("li",{class:W(["feed-item",{"feed-item--new":e.fresh,"feed-item--lit":e.lit,"feed-item--open":y.value}]),onClick:E},[o("span",{class:W(["feed-item__icon",p(fe)("feed-item__icon",v.value)])},h(n.value),3),o("div",Na,[o("div",ja,[O(ne,{text:e.item.ref,url:e.item.url},null,8,["text","url"]),o("span",{class:W(["feed-item__kind",{"feed-item__kind--bad":u.value==="bad"}])},h(e.item.kind),3),e.item.who?(r(),i("span",Pa,h(e.item.who),1)):C("",!0),e.item.mention?(r(),i("span",Fa,"@you")):C("",!0),o("span",za,[o("span",qa,h(A.value),1),_.value?(r(),i("button",{key:0,type:"button",class:"feed-item__toggle","aria-label":y.value?"Collapse":"Expand","aria-expanded":y.value,onClick:K(R,["stop"])},null,8,Ya)):C("",!0)])]),y.value?(r(),i(V,{key:0},[l.value?(r(),i("span",La,h(l.value),1)):C("",!0),o("div",Ha,h(e.item.body),1),o("div",Xa,[o("button",{type:"button",class:"btn btn--tonal",onClick:d[0]||(d[0]=K(k=>a("open",e.item),["stop"]))}," Open formatted "),O(de,{query:{ws:p(m).workspace.id,kind:"feed",ref:e.item.id}},null,8,["query"]),O(ne,{text:"Open in "+e.source.label,url:e.item.url,class:"btn"},null,8,["text","url"])])],64)):c.value?(r(),i("span",Ka,h(c.value),1)):C("",!0)])],2))}}),ft=j(Ga,[["__scopeId","data-v-27e556e1"]]),Ja=Le({source:"all",view:Me("wt-feed-view","ticket",{writeDefaults:!1}),query:"",openGroups:new Set}),Za=()=>Ja,Qa={key:0,class:"feed__new-count"},el={class:"feed__bar"},tl={key:0,class:"alert alert--inline",role:"alert"},sl={class:"feed__search"},nl={class:"feed__search-count"},ol={key:1,class:"empty"},al={key:2,class:"feed__list"},ll=["title","onClick"],rl=["aria-expanded","aria-label"],il={class:"feed__group-text"},cl={class:"feed__group-key"},dl={key:0,class:"feed__group-sum"},ul={class:"feed__group-meta"},pl={class:"feed__group-kinds"},_l={class:"timestamp feed__group-when"},hl={key:0,class:"feed__children"},fl=U({__name:"Feed",emits:["open"],setup(e,{emit:t}){const s=t,a=Za(),n=[{id:"ticket",label:"By ticket"},{id:"flat",label:"Flat"}],l=g(()=>m.workspace.connectors||[]),c=g(()=>Object.fromEntries(l.value.map($=>[$.id,$]))),u=$=>c.value[$]||{id:$,type:$,label:$},v=g(()=>l.value.length>1?[{id:"all",label:"All"}].concat(l.value.map($=>({id:$.id,label:$.label}))):[]),_=g({get:()=>v.value.some($=>$.id===a.source)?a.source:"all",set:$=>{a.source=$}}),y=g(()=>({base:m.marks.base,seen:new Set(m.marks.seen)})),A=$=>Ft($,y.value),E=g(()=>m.feed.filter(A).length),R=g(()=>m.feed.filter($=>_.value==="all"||$.source===_.value)),S=g(()=>R.value.filter($=>Ra($,a.query)).slice(0,200)),d=g(()=>new Map(m.turn.map($=>[$.key,$]))),k=g(()=>Da(m.turn)),f=g(()=>(ce.value,Oa(S.value,y.value).map($=>({...$,row:d.value.get($.id),move:k.value.has($.id),when:ae($.newest),kinds:Ia($.items),open:a.openGroups.has($.id)||!!a.query})))),B=($,Y)=>{$.has(Y)?$.delete(Y):$.add(Y)},z=$=>B(a.openGroups,$.id),L=$=>B(b.open,$),J=$=>qe({seen:[$]}).then(se),ue=D("");Qe(()=>{ue.value=""});const Q=async()=>{ue.value=await qe({seen:m.feed.map($=>$.id),base:Date.now()})};return($,Y)=>(r(),q(Re,{id:"feed",title:"What changed",note:"Newest first, every source",tip:"Newest first: comments, reviews, merges and failed checks on your PRs, and ticket changes. Each item opens where it happened.",class:"feed",column:""},{head:te(()=>[E.value?(r(),i("span",Qa,h(E.value)+" new",1)):C("",!0)]),default:te(()=>[o("div",el,[v.value.length?(r(),q(Te,{key:0,modelValue:_.value,"onUpdate:modelValue":Y[0]||(Y[0]=M=>_.value=M),options:v.value,label:"Feed source"},null,8,["modelValue","options"])):C("",!0),O(Te,{modelValue:p(a).view,"onUpdate:modelValue":Y[1]||(Y[1]=M=>p(a).view=M),options:n,label:"Feed view"},null,8,["modelValue"])]),ue.value?(r(),i("p",tl,h(ue.value),1)):C("",!0),o("div",sl,[He(o("input",{"onUpdate:modelValue":Y[2]||(Y[2]=M=>p(a).query=M),class:"input feed__search-input",type:"search",placeholder:"Search the feed: key, author, words…","aria-label":"Search the feed",autocomplete:"off"},null,512),[[Xe,p(a).query,void 0,{trim:!0}]]),o("span",nl,h(p(a).query?S.value.length+" of "+R.value.length:""),1),o("button",{type:"button",class:"feed__mark-read",onClick:Q},"Mark all read")]),p(m).feed.length?(r(),i("ol",al,[p(a).view==="ticket"?(r(!0),i(V,{key:0},N(f.value,M=>(r(),i("li",{key:M.id,class:W(["feed__group",{"feed__group--new":M.unread,"feed__group--open":M.open,"feed__group--move":M.move}])},[o("div",{class:"feed__group-head",title:M.move?"Your move on this ticket":void 0,onClick:Z=>z(M)},[o("button",{type:"button",class:"feed__group-caret","aria-expanded":M.open,"aria-label":"Collapse or expand "+M.id}," › ",8,rl),o("div",il,[o("div",cl,[O(ne,{text:M.id,url:M.row?M.row.ticket_url:M.items[0].url,class:"ticket-key feed__group-ticket"},null,8,["text","url"]),M.row||M.title?(r(),i("span",dl,h(M.row?M.row.summary:M.title),1)):C("",!0)]),o("div",ul,[o("span",{class:W(["feed__group-count",{"feed__group-count--new":M.unread}])},h(M.unread?M.unread+" new":M.items.length),3),o("span",pl,h(M.kinds),1),o("span",_l,h(M.when),1)])]),O(de,{class:"feed__group-copy",query:{ws:p(m).workspace.id,kind:"thread",ref:M.id}},null,8,["query"])],8,ll),M.open?(r(),i("ul",hl,[(r(!0),i(V,null,N(M.items,Z=>(r(),q(ft,{key:Z.id,item:Z,source:u(Z.source),fresh:A(Z),lit:!!M.unread,expanded:p(b).open.has(Z.id),onOpen:Y[3]||(Y[3]=Ee=>s("open",Ee)),onToggle:L,onSeen:J},null,8,["item","source","fresh","lit","expanded"]))),128))])):C("",!0)],2))),128)):(r(!0),i(V,{key:1},N(S.value,M=>(r(),q(ft,{key:M.id,item:M,source:u(M.source),fresh:A(M),expanded:p(b).open.has(M.id),onOpen:Y[4]||(Y[4]=Z=>s("open",Z)),onToggle:L,onSeen:J},null,8,["item","source","fresh","expanded"]))),128))])):(r(),i("p",ol," No changes yet. New comments, reviews and status changes show up here. "))]),_:1}))}}),vl=j(fl,[["__scopeId","data-v-d92a7c74"]]),ml={id:"first-run",class:"island first-run","aria-labelledby":"first-run-title"},kl={class:"first-run__text"},yl={class:"first-run__actions"},gl=["aria-disabled"],bl=U({__name:"FirstRun",setup(e){const t=D(!1),s=()=>Js("connectors"),a=()=>{t.value||(t.value=!0,ln().finally(()=>{t.value=!1}))};return(n,l)=>(r(),i("section",ml,[l[1]||(l[1]=o("h2",{id:"first-run-title",class:"first-run__title"}," Whose Turn watches your tickets and pull requests and shows what is waiting on you. ",-1)),o("p",kl,[o("b",null,h(p(m).workspace.name),1),l[0]||(l[0]=F(" has no sources yet, so there is nothing to watch. Connect a repository or a tracker, or look around a demo workspace with invented data first. ",-1))]),o("div",yl,[o("button",{type:"button",class:"btn btn--tonal",onClick:s},"Add a source"),o("button",{type:"button",class:"btn","aria-disabled":t.value,onClick:a}," Try a demo workspace ",8,gl)]),l[2]||(l[2]=o("p",{class:"first-run__text first-run__ai"},' Or ask Claude: "add my GitHub repository to Whose Turn." ',-1))]))}}),wl=j(bl,[["__scopeId","data-v-ef746675"]]),$l=new RegExp("(`[^`]{1,500}`)|(\\*\\*[^*]{1,500}\\*\\*)|(\\[[^\\]]{1,300}\\]\\(https?:\\/\\/[^)\\s]{1,2000}\\))|(https?:\\/\\/[^\\s<>()]{1,2000})|(@[A-Za-z0-9-]+(?: [A-Z][a-z]+)?)|((?<![A-Za-z0-9])\\*[^*\\s][^*]{0,500}\\*(?![A-Za-z0-9])|(?<![A-Za-z0-9])_[^_\\s][^_]{0,500}_(?![A-Za-z0-9]))","g"),vt=(e,t)=>{const s=Ge(t);return s?{type:"link",text:e,href:s}:{type:"text",text:e}},xl=e=>{const t=e[0];if(e[1])return{type:"code",text:t.slice(1,-1)};if(e[2])return{type:"strong",text:t.slice(2,-2)};if(e[3]){const s=t.indexOf("](");return vt(t.slice(1,s),t.slice(s+2,-1))}return e[4]?vt(t,t):e[5]?{type:"mention",text:t}:{type:"em",text:t.slice(1,-1)}},ge=e=>{const t=new RegExp($l),s=[];let a=0,n;for(;n=t.exec(e);)n.index>a&&s.push({type:"text",text:e.slice(a,n.index)}),s.push(xl(n)),a=n.index+n[0].length;return a<e.length&&s.push({type:"text",text:e.slice(a)}),s},Cl=e=>{const t=String(e||"").replace(/<!--[\s\S]*?-->/g,"").split(`
|
|
2
|
+
`),s=[];let a=[],n=null;const l=()=>{a.length&&(s.push({type:"paragraph",lines:a.map(ge)}),a=[]),n=null};for(let c=0;c<t.length;c++){const u=t[c];if(/^\s*```/.test(u)){l();const A=[];for(c++;c<t.length&&!/^\s*```/.test(t[c]);c++)A.push(t[c]);s.push({type:"fence",text:A.join(`
|
|
3
|
+
`)});continue}const v=u.match(/^(#{1,6})\s+(.*)$/);if(v){l(),s.push({type:"heading",level:v[1].length<=2?4:5,inline:ge(v[2])});continue}const _=u.match(/^>\s?(.*)$/);if(_){l(),s.push({type:"quote",inline:ge(_[1])});continue}const y=u.match(/^\s*([-*+]|\d+[.)])\s+(.*)$/);if(y){const A=n;a.length&&(l(),n=A);const E=/\d/.test(y[1]),R=n,S=R&&R.ordered===E?R:{type:"list",ordered:E,items:[]};S!==R&&s.push(S),n=S,S.items.push(ge(y[2]));continue}if(!u.trim()){l();continue}n=null,a.push(u)}return l(),s},we=(e,t,s="")=>{const a=document.createElement(e);return a.textContent=t,s&&(a.className=s),a},Sl=e=>{if(e.type==="text")return document.createTextNode(e.text);if(e.type==="link"){const t=we("a",e.text);return t.setAttribute("href",e.href),t.setAttribute("target","_blank"),t.setAttribute("rel","noopener"),t}return e.type==="mention"?we("span",e.text,"markdown-view__mention"):we(e.type,e.text)},qt=(e,t)=>{e.forEach(s=>t.append(Sl(s)))},We=(e,t)=>{const s=document.createElement(e);return qt(t,s),s},Tl=e=>{if(e.type==="fence"){const s=document.createElement("pre");return s.append(we("code",e.text)),s}if(e.type==="heading")return We(`h${e.level}`,e.inline);if(e.type==="quote")return We("blockquote",e.inline);if(e.type==="list"){const s=document.createElement(e.ordered?"ol":"ul");return e.items.forEach(a=>s.append(We("li",a))),s}const t=document.createElement("p");return e.lines.forEach((s,a)=>{a&&t.append(document.createElement("br")),qt(s,t)}),t},Ml=e=>{const t=document.createDocumentFragment();return Cl(e).forEach(s=>t.append(Tl(s))),t},Al=U({__name:"MarkdownView",props:{source:{default:""}},setup(e){const t=e,s=D(null);return Qt(()=>{s.value&&s.value.replaceChildren(Ml(t.source))}),(a,n)=>(r(),i("div",{ref_key:"host",ref:s,class:"markdown-view"},null,512))}}),Rl=j(Al,[["__scopeId","data-v-dcf42e95"]]),El={class:"modal__card"},Ol={class:"modal__head"},Dl={id:"modal-title",class:"modal__title"},Il=["href"],Bl=U({__name:"CommentModal",setup(e){const t=g(()=>b.modal),s=g(()=>t.value?[t.value.ref,t.value.kind,t.value.who].filter(Boolean).join(" · "):""),a=g(()=>t.value?Ge(t.value.url):""),n=g(()=>{var u;const c=(m.workspace.connectors||[]).find(v=>{var _;return v.id===((_=t.value)==null?void 0:_.source)});return"Open in "+(c?c.label:(u=t.value)==null?void 0:u.source)}),l=()=>{b.modal=null};return(c,u)=>t.value?(r(),i("div",{key:0,class:"modal",role:"dialog","aria-modal":"true","aria-labelledby":"modal-title",onClick:K(l,["self"])},[o("div",El,[o("div",Ol,[o("h3",Dl,h(s.value),1),a.value?(r(),i("a",{key:0,class:"btn",href:a.value,target:"_blank",rel:"noopener"},h(n.value),9,Il)):C("",!0),o("button",{type:"button",class:"btn btn--tonal",onClick:l},"Close")]),O(Rl,{source:t.value.body||t.value.text||""},null,8,["source"])])])):C("",!0)}}),Vl={class:"modal__card feedback-sheet__card"},Wl={class:"modal__head"},Ul={class:"modal__meta"},Nl=["aria-disabled"],jl={key:0,class:"alert"},Pl={class:"sheet-form"},Fl=["placeholder","onKeydown"],zl={key:0,class:"feedback-sheet__note"},ql={key:1,class:"feedback-sheet__note"},Yl={key:2,class:"empty"},Ll=["href"],Hl={key:3,class:"feedback-sheet__reports"},Xl=["href"],Kl={class:"feedback-sheet__report-title"},Gl={class:"timestamp"},Jl=U({__name:"FeedbackSheet",setup(e){const t=D(""),s=D(null),a=D(!1),n=D([]),l=D(null),c=D(""),u=D(!0),v=Ae("",1600),_=D([]),y=d=>"/report?id="+encodeURIComponent(d),A=()=>he("/api/feedback").then(d=>{u.value=d.configured,c.value=d.configured?"Linear · "+d.project:"not connected",_.value=d.reports||[]},()=>{n.value=[$e+"; past reports cannot be loaded."]}),E=()=>{b.feedback=!1},R=()=>{const d=t.value.trim();if(!d){n.value=["Write at least one line."];return}n.value=[],navigator.clipboard.writeText(d).then(()=>{v.value="sent"},()=>{v.value="failed"})},S=()=>{if(!u.value||a.value)return;const d=t.value.trim();if(!d){n.value=["Write at least one line."];return}a.value=!0,oe("/api/feedback",{text:d,from:"the window",ws:m.workspace.id,kind_ref:{kind:"workspace",ref:m.workspace.id}}).then(k=>{if(a.value=!1,k.error){n.value=St(k.error);return}l.value=k.issue||null,t.value="",_.value=k.reports||_.value}).catch(k=>{a.value=!1,n.value=[le(k,{refused:"Not sent",unreachable:Pe})]})};return X(()=>b.feedback,d=>{d&&(n.value=[],l.value=null,A().then(()=>kt(()=>{var k;return(k=s.value)==null?void 0:k.focus()})))}),(d,k)=>p(b).feedback?(r(),i("div",{key:0,class:"modal",role:"dialog","aria-modal":"true","aria-labelledby":"feedback-title",onClick:K(E,["self"])},[o("div",Vl,[o("div",Wl,[k[1]||(k[1]=o("h3",{id:"feedback-title",class:"modal__title"},"To the Whose Turn backlog",-1)),o("span",Ul,h(c.value),1),o("button",{type:"button",class:"btn btn--tonal","aria-disabled":!u.value||a.value,onClick:S},h(a.value?"Composing…":"Send"),9,Nl),o("button",{type:"button",class:"btn",onClick:E},"Close")]),n.value.length?(r(),i("div",jl,h(n.value.join(" · ")),1)):C("",!0),o("div",Pl,[He(o("textarea",{ref_key:"field",ref:s,"onUpdate:modelValue":k[0]||(k[0]=f=>t.value=f),class:"input feedback-sheet__text",rows:"5",placeholder:"Describe the problem or the wish in your own words."+(u.value?" Cmd+Enter sends.":""),autocomplete:"off",onKeydown:[je(K(S,["meta","prevent"]),["enter"]),je(K(S,["ctrl","prevent"]),["enter"])]},null,40,Fl),[[Xe,t.value]]),u.value?(r(),i("p",ql," An assistant turns it into a proper issue (title, kind, description) and files it in the Whose Turn backlog with this workspace's summary attached. ")):(r(),i("p",zl,[k[2]||(k[2]=F(" The maintainer's tracker is not connected on this machine, so nothing can be sent from here. Copy your remark and pass it on yourself. ",-1)),o("button",{type:"button",class:"btn",onClick:R},h(p(v)==="sent"?"Copied":p(v)==="failed"?"Could not copy":"Copy remark"),1)])),l.value?(r(),i("p",Yl,[k[3]||(k[3]=F(" Filed as ",-1)),o("a",{href:y(l.value.identifier),target:"_blank",rel:"noopener"},h(l.value.identifier),9,Ll),F(" · "+h(l.value.title),1)])):C("",!0),_.value.length?(r(),i("div",Hl,[k[4]||(k[4]=o("h4",{class:"sheet-form__heading"},"Your reports",-1)),(r(!0),i(V,null,N(_.value,f=>(r(),i("div",{key:f.id,class:"feedback-sheet__report"},[o("a",{href:y(f.id),target:"_blank",rel:"noopener"},h(f.id),9,Xl),o("span",Kl,h(f.title),1),o("span",{class:W(["pill",p(fe)("pill",p(xs)(f.outcome))])},h(f.state||"filed"),3),o("span",Gl,h(p(ae)(f.changed_at||f.filed_at)),1)]))),128))])):C("",!0)])])])):C("",!0)}}),Zl=j(Jl,[["__scopeId","data-v-ec34adcc"]]),Ql={class:"field-row"},er={class:"field-row__label"},tr={key:0,class:"field-row__check"},sr=["checked"],nr={key:2,class:"field-row__inline"},or=["value"],ar=["value"],lr=["value"],rr=["placeholder","value"],ir={key:5,class:"field-row__hint"},cr=U({__name:"FieldRow",props:Ne({spec:{},placeholder:{default:""},hint:{default:""}},{modelValue:{type:[String,Array,Boolean]},modelModifiers:{}}),emits:Ne(["secret"],["update:modelValue"]),setup(e,{emit:t}){const s=e,a=t,n=mt(e,"modelValue"),l=/^#[0-9a-f]{6}$/i,c=()=>String(n.value??""),u=()=>n.value||[],v=R=>{n.value=R},_=()=>n.value??s.spec.default??!1,y=R=>{n.value=R===(s.spec.default??!1)?void 0:R},A=R=>R.target.value,E=R=>v(R.split(",").map(S=>S.trim()).filter(Boolean));return(R,S)=>(r(),i("div",Ql,[o("label",er,h(e.spec.label)+h(e.spec.required?" *":""),1),e.spec.boolean?(r(),i("label",tr,[o("input",{type:"checkbox",checked:_(),onChange:S[0]||(S[0]=d=>y(d.target.checked))},null,40,sr),F(" "+h(e.hint||e.spec.hint),1)])):e.spec.secret?(r(),i("input",{key:1,class:"input",type:"password",placeholder:"paste to replace",autocomplete:"off",onInput:S[1]||(S[1]=d=>a("secret",e.spec.key,A(d)))},null,32)):e.spec.colour?(r(),i("div",nr,[o("input",{class:"input field-row__colour",type:"color",value:l.test(c())?c():"#888888",onInput:S[2]||(S[2]=d=>v(A(d)))},null,40,or),o("input",{class:"input",type:"text",placeholder:"#rrggbb",value:c(),onInput:S[3]||(S[3]=d=>v(A(d)))},null,40,ar),o("button",{type:"button",class:"btn",onClick:S[4]||(S[4]=d=>v(""))},"None")])):e.spec.list?(r(),i("input",{key:3,class:"input",type:"text",value:u().join(", "),onInput:S[5]||(S[5]=d=>E(A(d)))},null,40,lr)):(r(),i("input",{key:4,class:"input",type:"text",placeholder:e.placeholder,value:c(),onInput:S[6]||(S[6]=d=>v(A(d)))},null,40,rr)),!e.spec.boolean&&(e.hint||e.spec.hint)?(r(),i("small",ir,h(e.hint||e.spec.hint),1)):C("",!0)]))}}),ie=j(cr,[["__scopeId","data-v-2ba0ba86"]]),dr={class:"modal__card settings-sheet__card"},ur={class:"modal__head"},pr={class:"modal__meta"},_r={key:0,class:"settings-sheet__dirty"},hr={key:0,class:"settings-sheet__confirm",role:"alertdialog"},fr={class:"settings-sheet__confirm-text"},vr={key:1,class:"alert settings-sheet__alert",role:"alert"},mr={key:2,class:"alert settings-sheet__alert"},kr={key:3,class:"settings-sheet__body"},yr={class:"settings-sheet__list"},gr=["aria-current","onClick"],br={class:"sheet-form"},wr={key:0,class:"empty"},$r={ref:"connectors-head",class:"sheet-form__heading"},xr={class:"settings-sheet__connector-head"},Cr={class:"settings-sheet__connector-name"},Sr=["onClick"],Tr={class:"settings-sheet__actions"},Mr=["onClick"],Ar={key:0,class:"settings-sheet__rules"},Rr={class:"settings-sheet__rule-when"},Er={class:"settings-sheet__rule-then"},Or={key:1,class:"empty"},Dr=U({__name:"SettingsSheet",setup(e){const t=D([]),s=D(0),a=D([]),n=D({workspace:[],theme:[],rules:[],statuses:[]}),l=D(""),c=D([]),u=D({}),v=es("connectors-head"),_=new Map,y=D(0),A=D(""),E=D(null),R=D(""),S=g(()=>b.settings&&!R.value&&(y.value>0||dt(t.value)!==A.value)),d={key:"id",label:"Entry id"},k={key:"label",label:"Label"},f=g(()=>t.value[s.value]),B=g(()=>(f.value&&u.value[f.value.id]||[]).map(Ys)),z=w=>a.value.find(T=>T.type===w.type)||{type:w.type,label:w.type,fields:[]},L=g(()=>t.value.map((w,T)=>({uid:ct(w),label:w.name||w.id||"Workspace "+(T+1)}))),J=g(()=>{var w;return(((w=f.value)==null?void 0:w.connectors)||[]).map(T=>({entry:T,uid:ct(T),spec:z(T)}))}),ue=w=>{var x;const T=(x=m.workspaces.find(P=>P.id===w))==null?void 0:x.theme_seed;return T?`empty: ${T}, derived from the workspace id`:"empty derives one from the workspace id"},Q=(w,T)=>w[T],$=(w,T,x)=>{_.set(w,Object.assign(_.get(w)||{},{[T]:x})),y.value+=1},Y=()=>{t.value.push(rt({id:"new-"+(t.value.length+1),name:"New workspace"})),s.value=t.value.length-1},M=()=>{t.value.splice(s.value,1),s.value=Math.max(0,s.value-1)},Z=()=>{f.value&&(E.value={text:`Delete ${f.value.name||f.value.id}? Its to-dos and marks stay on disk.`,yes:"Delete",no:"Keep",run:M})},Ee=w=>{var T;return(T=f.value)==null?void 0:T.connectors.push({type:w.type})},Yt=w=>{f.value&&(_.delete(f.value.connectors[w]),f.value.connectors.splice(w,1))},Lt=w=>{if(!f.value)return;const T=z(f.value.connectors[w]).label;E.value={text:`Remove the ${T} connector from ${f.value.name||f.value.id}?`,yes:"Remove",no:"Keep",run:()=>Yt(w)}},Ht=()=>he("/api/config").then(w=>{R.value="",t.value=(w.workspaces||[]).map(rt),a.value=w.connectors||[],n.value=w.fields,_.clear(),y.value=0,E.value=null,s.value=Math.max(0,t.value.findIndex(T=>T.id===m.workspace.id)),l.value=w.config_file||"",c.value=w.problems||[],u.value=w.tables||{},A.value=dt(t.value)}).catch(()=>{R.value=$e+"; settings cannot be loaded."}).then(kt).then(()=>{var w;b.settingsAt==="connectors"&&((w=v.value)==null||w.scrollIntoView({block:"start"})),b.settingsAt=""}),Oe=()=>{b.settings=!1},tt=()=>{E.value={text:"Discard changes?",yes:"Discard",no:"Keep editing",run:Oe}},st=()=>S.value?tt():Oe(),nt=w=>{const T=E.value;E.value=null,w&&(T==null||T.run())},Xt=async()=>{try{const w=await oe("/api/config",{workspaces:t.value.map(T=>Ws(T)),secrets:Ns(t.value,_)});if(w.error){c.value=St(w.error);return}Oe(),It()}catch(w){c.value=[le(w,{unreachable:$e+"; your changes are kept, save again."})]}};return X(S,w=>{b.settingsDirty=w},{immediate:!0}),X(()=>b.settingsAsk,w=>{w&&(b.settingsAsk=!1,tt())}),X(()=>b.settings,w=>{w&&Ht()}),(w,T)=>p(b).settings?(r(),i("div",{key:0,class:"modal",role:"dialog","aria-modal":"true","aria-labelledby":"settings-title",onClick:K(st,["self"])},[o("div",dr,[o("div",ur,[T[2]||(T[2]=o("h3",{id:"settings-title",class:"modal__title"},"Workspaces",-1)),o("span",pr,h(l.value),1),S.value?(r(),i("span",_r,"Unsaved changes")):C("",!0),R.value?C("",!0):(r(),i("button",{key:1,type:"button",class:"btn btn--tonal",onClick:Xt},"Save")),o("button",{type:"button",class:"btn",onClick:st},"Close")]),E.value?(r(),i("div",hr,[o("span",fr,h(E.value.text),1),o("button",{type:"button",class:"btn btn--danger",onClick:T[0]||(T[0]=x=>nt(!0))},h(E.value.yes),1),o("button",{type:"button",class:"btn",onClick:T[1]||(T[1]=x=>nt(!1))},h(E.value.no),1)])):C("",!0),R.value?(r(),i("div",vr,h(R.value),1)):c.value.length?(r(),i("div",mr,h(c.value.join(" · ")),1)):C("",!0),R.value?C("",!0):(r(),i("div",kr,[o("ul",yr,[(r(!0),i(V,null,N(L.value,(x,P)=>(r(),i("li",{key:x.uid},[o("button",{type:"button",class:"settings-sheet__workspace","aria-current":P===s.value,onClick:me=>s.value=P},h(x.label),9,gr)]))),128)),o("li",null,[o("button",{type:"button",class:"settings-sheet__workspace settings-sheet__workspace--add",onClick:Y}," + Add workspace ")])]),o("div",br,[f.value?(r(),i(V,{key:1},[T[3]||(T[3]=o("h4",{class:"sheet-form__heading"},"Workspace",-1)),(r(!0),i(V,null,N(n.value.workspace,x=>(r(),q(ie,{key:x.key,spec:x,"model-value":Q(f.value,x.key),"onUpdate:modelValue":P=>p(re)(f.value,x.key,P)},null,8,["spec","model-value","onUpdate:modelValue"]))),128)),T[4]||(T[4]=o("h4",{class:"sheet-form__heading"},"Theme",-1)),(r(!0),i(V,null,N(n.value.theme,x=>{var P;return r(),q(ie,{key:x.key,spec:x,hint:x.key==="seed"&&!((P=f.value.theme)!=null&&P.seed)?ue(f.value.id):"","model-value":Q(f.value.theme,x.key),"onUpdate:modelValue":me=>p(re)(f.value.theme,x.key,me)},null,8,["spec","hint","model-value","onUpdate:modelValue"])}),128)),o("h4",$r,"Connectors",512),(r(!0),i(V,null,N(J.value,({entry:x,spec:P,uid:me},Kt)=>(r(),i("div",{key:me,class:"settings-sheet__connector"},[o("div",xr,[o("strong",Cr,h(P.label),1),o("button",{type:"button",class:"btn btn--danger",onClick:ee=>Lt(Kt)}," Remove ",8,Sr)]),O(ie,{spec:d,placeholder:P.type,hint:"only needed when one type appears twice","model-value":Q(x,"id"),"onUpdate:modelValue":ee=>p(re)(x,"id",ee)},null,8,["placeholder","model-value","onUpdate:modelValue"]),O(ie,{spec:k,placeholder:P.label,hint:"shown in the feed filter","model-value":Q(x,"label"),"onUpdate:modelValue":ee=>p(re)(x,"label",ee)},null,8,["placeholder","model-value","onUpdate:modelValue"]),(r(!0),i(V,null,N(P.fields,ee=>(r(),q(ie,{key:ee.key,spec:ee,"model-value":Q(x,ee.key),"onUpdate:modelValue":De=>p(re)(x,ee.key,De),onSecret:(De,Gt)=>$(x,De,Gt)},null,8,["spec","model-value","onUpdate:modelValue","onSecret"]))),128))]))),128)),o("div",Tr,[(r(!0),i(V,null,N(a.value,x=>(r(),i("button",{key:x.type,type:"button",class:"btn",onClick:P=>Ee(x)}," + "+h(x.label),9,Mr))),128))]),T[5]||(T[5]=o("h4",{class:"sheet-form__heading"},"Rules",-1)),(r(!0),i(V,null,N(n.value.rules,x=>(r(),q(ie,{key:x.key,spec:x,"model-value":Q(f.value.rules,x.key),"onUpdate:modelValue":P=>p(re)(f.value.rules,x.key,P)},null,8,["spec","model-value","onUpdate:modelValue"]))),128)),(r(!0),i(V,null,N(n.value.statuses,x=>(r(),q(ie,{key:x.key,spec:x,"model-value":Q(f.value.rules.statuses,x.key),"onUpdate:modelValue":P=>p(re)(f.value.rules.statuses,x.key,P)},null,8,["spec","model-value","onUpdate:modelValue"]))),128)),T[6]||(T[6]=o("h4",{class:"sheet-form__heading"},"Whose turn table",-1)),T[7]||(T[7]=o("p",{class:"settings-sheet__rules-note"}," The rows in force, first match wins. Read-only here: ask Claude to change rules.table through /api/config. ",-1)),B.value.length?(r(),i("ol",Ar,[(r(!0),i(V,null,N(B.value,(x,P)=>(r(),i("li",{key:P,class:"settings-sheet__rule"},[o("span",Rr,h(x.when),1),o("span",Er,h(x.then),1)]))),128))])):(r(),i("p",Or,"Save the workspace to see its rows.")),o("div",{class:"settings-sheet__actions"},[o("button",{type:"button",class:"btn btn--danger",onClick:Z}," Delete workspace ")])],64)):(r(),i("p",wr,"No workspaces yet. Add one on the left."))])]))])])):C("",!0)}}),Ir=j(Dr,[["__scopeId","data-v-779d567d"]]),Br={class:"app__shell"},Vr=["inert"],Wr={id:"wt",class:"dashboard dashboard--fit"},Ur=["inert"],Nr={class:"dashboard__main"},jr=U({__name:"App",setup(e){const t=g(()=>!!m.workspace.id&&!m.workspace.connectors.length),s=g(()=>Dt.value!==null);return X(ut,Is,{flush:"post"}),hs("Escape",Zs,{target:document}),ts(()=>{Gs()}),(a,n)=>(r(),i("div",{class:"app",style:ss(p(ut))},[O(Cn,{inert:s.value,onSettings:n[0]||(n[0]=l=>p(b).settings=!0)},null,8,["inert"]),o("div",Br,[O(qn,{inert:s.value,onReport:n[1]||(n[1]=l=>p(b).feedback=!0)},null,8,["inert"]),p(pt).length?(r(),i("div",{key:0,class:"alert",inert:s.value},h(p(pt).join(" · ")),9,Vr)):C("",!0),o("div",Wr,[n[3]||(n[3]=o("canvas",{id:"backdrop",class:"dashboard__backdrop","aria-hidden":"true"},null,-1)),O(mo,{inert:s.value},null,8,["inert"]),t.value?(r(),q(wl,{key:0,inert:s.value},null,8,["inert"])):(r(),i("div",{key:1,class:"dashboard__layout",inert:s.value},[o("div",Nr,[O(ra,{class:"dashboard__move"}),O(ya,{class:"dashboard__todo"}),O(Ma,{class:"dashboard__waiting"})]),O(vl,{class:"dashboard__feed",onOpen:n[2]||(n[2]=l=>p(b).modal=l)})],8,Ur)),O(Zl),O(Ir),O(Bl)])])],4))}}),Pr=j(jr,[["__scopeId","data-v-f146b085"]]);ns(Pr).mount("#app");nn();Bs();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.rail[data-v-be863916]{position:fixed;inset:0 auto 0 0;z-index:30;box-sizing:border-box;display:flex;flex-direction:column;gap:4px;width:60px;padding:12px 8px;overflow:hidden;border-right:1px solid var(--glass-edge);background:rgb(var(--glass),.72);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);font-family:var(--md-font);color:var(--md-on-surface);transition:width .18s ease,box-shadow .18s ease}.rail[data-v-be863916]:hover,.rail[data-v-be863916]:has(:focus-visible){width:232px;box-shadow:12px 0 32px #0000001f}.app:has(.modal) .rail[data-v-be863916]{z-index:0}.rail__item[data-v-be863916]{position:relative;display:flex;align-items:center;gap:12px;height:44px;padding:0 4px;border-radius:12px;color:inherit;text-decoration:none;white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none}.rail__item[data-v-be863916]:focus-visible{outline:var(--focus-ring);outline-offset:-2px}.rail__item--dragging[data-v-be863916]{opacity:.4}.rail__item[data-drop=before][data-v-be863916]{box-shadow:0 -2px 0 0 var(--md-primary)}.rail__item[data-drop=after][data-v-be863916]{box-shadow:0 2px 0 0 var(--md-primary)}.rail__item[data-v-be863916]:hover{background:var(--state-hover)}.rail__item[aria-selected=true][data-v-be863916]{background:var(--md-secondary-container);color:var(--md-on-secondary-container)}.rail__icon[data-v-be863916]{flex:none;display:block;width:36px;height:36px;border-radius:10px}.rail__gear[data-v-be863916]{flex:none;display:grid;place-items:center;width:36px;height:36px;color:var(--md-on-surface-variant)}.rail__label[data-v-be863916]{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:14px;font-weight:500;opacity:0;transition:opacity .12s ease}.rail:hover .rail__label[data-v-be863916],.rail:has(:focus-visible) .rail__label[data-v-be863916]{opacity:1}.rail__badge[data-v-be863916]{position:absolute;top:2px;left:30px;box-sizing:border-box;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--md-primary);color:var(--md-on-primary);font-size:10px;font-weight:700;line-height:18px;text-align:center}.rail:hover .rail__badge[data-v-be863916],.rail:has(:focus-visible) .rail__badge[data-v-be863916]{position:static;flex:none}.rail__spacer[data-v-be863916]{flex:1}.ai-button[data-v-d32a60c7]{display:inline-grid;place-items:center;width:26px;height:26px;border-radius:50%;cursor:pointer;color:var(--md-primary);background:rgb(var(--md-state),.06);vertical-align:middle;transition:background .12s ease,transform .12s ease}.ai-button__icon[data-v-d32a60c7]{width:15px;height:15px}.ai-button[data-v-d32a60c7]:hover{background:var(--md-secondary-container);transform:scale(1.08)}.ai-button[data-v-d32a60c7]:focus-visible{outline:var(--focus-ring);outline-offset:1px}.ai-button.ai-button--sent[data-v-d32a60c7]{background:var(--md-tertiary-container);color:var(--md-on-tertiary-container)}.ai-button.ai-button--failed[data-v-d32a60c7]{background:var(--md-error-container);color:var(--md-on-error-container)}.ai-button--big[data-v-d32a60c7]{width:32px;height:32px;background:rgb(var(--glass),.7)}.ai-button--big .ai-button__icon[data-v-d32a60c7]{width:18px;height:18px}.update-chip[data-v-f70d2aed]{display:inline-flex;align-items:center;height:26px;padding:0 12px;font:inherit;color:var(--md-on-primary-container);cursor:pointer;background:var(--md-primary-container);border:0;border-radius:var(--md-r-chip)}.update-chip--busy[data-v-f70d2aed],.update-chip--done[data-v-f70d2aed]{cursor:default}.update-chip--busy[data-v-f70d2aed]{opacity:.7}.update-chip--failed[data-v-f70d2aed]{color:var(--md-on-error-container);background:var(--md-error-container)}.status-bar[data-v-eefe1de8]{display:flex;align-items:center;gap:10px;margin:0 4px 8px;font-family:var(--md-font);font-size:12px;color:var(--md-on-surface-variant)}.status-bar__btn[data-v-eefe1de8]{height:26px;padding:0 12px;font-size:12px;text-decoration:none}.status-bar__btn--busy[data-v-eefe1de8]{opacity:.6;pointer-events:none}.status-bar__report[data-v-eefe1de8]{gap:6px;color:var(--md-error);border-color:var(--md-error)}.status-bar__icon[data-v-eefe1de8]{width:16px;height:16px}.status-bar__live[data-v-eefe1de8]{width:8px;height:8px;border-radius:50%;background:var(--live-on);box-shadow:0 0 0 0 var(--live-pulse-rest);animation:status-bar-pulse-eefe1de8 2s infinite}.status-bar__live--off[data-v-eefe1de8]{background:var(--live-off);animation:none}.status-bar__live--warn[data-v-eefe1de8]{background:var(--live-warn);animation:none}.status-bar__notice[data-v-eefe1de8]{color:var(--live-off)}.status-bar__ai-hint[data-v-eefe1de8]{margin-left:-4px}@keyframes status-bar-pulse-eefe1de8{0%{box-shadow:0 0 0 0 var(--live-pulse-from)}70%{box-shadow:0 0 0 8px var(--live-pulse-to)}to{box-shadow:0 0 0 0 var(--live-pulse-to)}}@media(prefers-reduced-motion:reduce){.status-bar__live[data-v-eefe1de8]{animation:none}}.workspace-header[data-v-0f4b6d6e]{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,2fr) 32px;gap:8px;padding:8px;background:linear-gradient(135deg,var(--blob-1),var(--blob-2));color:var(--header-ink, var(--md-on-primary-container))}@media(prefers-reduced-transparency:reduce){.workspace-header[data-v-0f4b6d6e]{background:var(--md-s-low)}}.workspace-header__title[data-v-0f4b6d6e]{display:grid;align-content:center;gap:4px;padding:12px 16px}.workspace-header__name[data-v-0f4b6d6e]{margin:0;font-size:28px;font-weight:400;line-height:36px;color:var(--md-on-primary-container)}.workspace-header__line[data-v-0f4b6d6e]{margin:0;font-size:14px;opacity:.85}.workspace-header__count[data-v-0f4b6d6e]{font-weight:700;font-variant-numeric:tabular-nums}.workspace-header__give[data-v-0f4b6d6e]{display:flex;align-items:center;gap:8px;margin-top:8px}.workspace-header__links[data-v-0f4b6d6e]{display:inline-flex;flex-wrap:wrap;gap:6px;margin-left:10px}.workspace-header__links a[data-v-0f4b6d6e]{display:inline-flex;align-items:center;height:24px;padding:0 10px;border:1px solid var(--glass-edge);border-radius:999px;background:rgb(var(--glass),.55);color:inherit;font-size:12px;font-weight:500}.workspace-header__links a[data-v-0f4b6d6e]:hover{background:rgb(var(--glass),.85)}.workspace-header__fold[data-v-0f4b6d6e]{grid-row:1;grid-column:3;place-self:center center;margin:0;color:inherit}.workspace-header__tiles[data-v-0f4b6d6e]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}.workspace-header__tile[data-v-0f4b6d6e]{display:grid;align-content:start;gap:4px;padding:12px 16px;border:1px solid var(--glass-edge);border-radius:var(--md-r-card);background:rgb(var(--glass),calc(var(--glass-a) + .1));-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);color:var(--md-on-surface)}@media(prefers-reduced-transparency:reduce){.workspace-header__tile[data-v-0f4b6d6e]{background:var(--md-s-low);-webkit-backdrop-filter:none;backdrop-filter:none}}.workspace-header__tile-label[data-v-0f4b6d6e]{color:var(--md-on-surface-variant);font-size:12px;font-weight:500;line-height:16px;letter-spacing:.03em}.workspace-header__tile-value[data-v-0f4b6d6e]{min-height:40px;font-size:32px;font-weight:400;line-height:40px;font-variant-numeric:tabular-nums}.workspace-header__tile--you[data-v-0f4b6d6e]{border-color:transparent;background:var(--md-primary);color:var(--md-on-primary)}.workspace-header__tile--you .workspace-header__tile-label[data-v-0f4b6d6e]{color:var(--md-on-primary);opacity:.85}.workspace-header__tile--fail .workspace-header__tile-value[data-v-0f4b6d6e]{color:var(--md-error)}.workspace-header--collapsed[data-v-0f4b6d6e]{grid-template-columns:minmax(0,1fr) 32px;padding:4px 8px}.workspace-header--collapsed .workspace-header__title[data-v-0f4b6d6e]{display:flex;flex-wrap:wrap;align-items:baseline;gap:14px;padding:4px 8px}.workspace-header--collapsed .workspace-header__name[data-v-0f4b6d6e]{font-size:18px;line-height:28px}.workspace-header--collapsed .workspace-header__tiles[data-v-0f4b6d6e],.workspace-header--collapsed .workspace-header__give[data-v-0f4b6d6e],.workspace-header--collapsed .workspace-header__sentence[data-v-0f4b6d6e]{display:none}.workspace-header--collapsed .workspace-header__fold[data-v-0f4b6d6e]{grid-column:2}.workspace-header--collapsed .workspace-header__strip[data-v-0f4b6d6e]{font-size:13px}@media(max-width:900px){.workspace-header[data-v-0f4b6d6e],.workspace-header--collapsed[data-v-0f4b6d6e]{grid-template-columns:minmax(0,1fr) 32px}.workspace-header__fold[data-v-0f4b6d6e]{grid-column:2}.workspace-header__tiles[data-v-0f4b6d6e]{grid-column:1 / -1}}@media(max-width:640px){.workspace-header__tiles[data-v-0f4b6d6e]{grid-template-columns:repeat(2,minmax(0,1fr))}}.island__head[data-v-a03b7f62]{display:flex;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:16px;padding:12px 20px}.island__title[data-v-a03b7f62]{flex:none;margin:0;font-size:16px;line-height:24px;font-weight:500;letter-spacing:.01em;white-space:nowrap}.island__note[data-v-a03b7f62]{display:flex;flex:1 1 auto;align-items:center;justify-content:flex-end;gap:6px;min-width:0;margin:0;overflow:visible;color:var(--md-on-surface-variant);font-size:12px;line-height:16px;letter-spacing:.03em;text-align:right;white-space:nowrap;text-overflow:ellipsis}.island__note-text[data-v-a03b7f62]{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.island__body[data-v-a03b7f62]{padding:0 20px 12px}.island--column .island__head[data-v-a03b7f62]{gap:4px}.island--column .island__body[data-v-a03b7f62]{display:flex;flex-direction:column;min-height:0;padding-top:4px}.island.island--collapsed .island__body[data-v-a03b7f62]{display:none}@media(min-width:901px){.dashboard--fit .dashboard__main .island__body[data-v-a03b7f62]{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;padding-bottom:0}.dashboard--fit .dashboard__main .island.island--collapsed .island__body[data-v-a03b7f62]{display:none}}.segmented[data-v-7901b5d6]{display:inline-flex;overflow:hidden;border:1px solid var(--md-outline);border-radius:999px;vertical-align:middle}.segmented--chips[data-v-7901b5d6]{flex:none;align-self:flex-start;background:rgb(var(--glass),.4)}.segmented__option[data-v-7901b5d6]{display:inline-flex;align-items:center;gap:5px;height:26px;padding:0 11px;border-left:1px solid var(--md-outline);font-size:12px;font-weight:500;color:var(--md-on-surface);cursor:pointer;-webkit-user-select:none;user-select:none}.segmented__option[data-v-7901b5d6]:first-child{border-left:0}.segmented__count[data-v-7901b5d6]{font-weight:700;font-variant-numeric:tabular-nums}.segmented__option[aria-pressed=true][data-v-7901b5d6]{background:var(--md-secondary-container);color:var(--md-on-secondary-container)}.segmented__option[data-v-7901b5d6]:focus-visible{outline:var(--focus-ring);outline-offset:-2px}.segmented--chips .segmented__option[data-v-7901b5d6]{gap:normal;height:32px;padding:0 14px;font-family:var(--md-font);font-size:14px;letter-spacing:.01em}.segmented--chips .segmented__option[data-v-7901b5d6]:hover{background:var(--state-hover)}.segmented--chips .segmented__option[aria-pressed=true][data-v-7901b5d6]{background:var(--md-secondary-container);color:var(--md-on-secondary-container)}.ticket-cell__summary[data-v-8436de88]{display:block;max-width:44ch;margin-top:2px;color:var(--md-on-surface-variant);font-size:12px;line-height:16px;letter-spacing:.03em}.pr-cell[data-v-75c443c3]{display:flex;flex-direction:column;align-items:flex-start;gap:4px}.pr-cell__link[data-v-75c443c3]{max-width:100%;overflow-wrap:anywhere}.recently-done[data-v-12ee20e8]{display:flex;flex:0 1 auto;flex-direction:column;min-height:33px;margin:0 -20px;padding:0 20px;overflow:hidden;border-top:1px solid var(--md-outline-variant)}.recently-done[open][data-v-12ee20e8]{min-height:128px}.recently-done__summary[data-v-12ee20e8]{display:flex;flex:none;align-items:center;gap:4px;min-height:40px;list-style:none;color:var(--md-on-surface-variant);font-size:14px;font-weight:500;cursor:pointer}.recently-done__summary[data-v-12ee20e8]::-webkit-details-marker{display:none}.recently-done__summary[data-v-12ee20e8]:before{content:"▾";display:inline-grid;place-items:center;width:24px;font-size:18px;transform:rotate(-90deg);transition:transform .15s ease}.recently-done[open] .recently-done__summary[data-v-12ee20e8]:before{transform:none}.recently-done__count[data-v-12ee20e8]{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;margin-left:6px;padding:0 6px;border-radius:10px;background:var(--md-s-high);color:var(--md-on-surface-variant);font-size:12px;font-weight:700}.recently-done__list[data-v-12ee20e8]{flex:1 1 auto;min-height:0;max-height:min(30vh,240px);margin:0 -20px;padding-bottom:8px;overflow:auto}.row-scroll[data-v-7bd9ace7]{overflow-x:auto;margin:0 -20px}@media(min-width:901px){.dashboard--fit .dashboard__main .row-scroll[data-v-7bd9ace7]{flex:1 1 auto;min-height:0;overflow:auto}.dashboard--fit .row-scroll[data-v-7bd9ace7]::-webkit-scrollbar{width:8px;height:8px}.dashboard--fit .row-scroll[data-v-7bd9ace7]::-webkit-scrollbar-thumb{border-radius:4px;background:var(--md-outline-variant)}}.dashboard--fit .dashboard__main .row-scroll--floor[data-v-7bd9ace7]{min-height:5rem}.row-scroll__more[data-v-7bd9ace7]{flex:none;display:block;margin:0 -20px;padding:4px 20px 6px;border-top:1px solid var(--md-outline-variant);color:var(--md-primary);font-size:13px;font-weight:500;cursor:pointer}.row-scroll__more[data-v-7bd9ace7]:hover{background:rgb(var(--md-state),.06)}.row-scroll__more[data-v-7bd9ace7]:focus-visible{outline:var(--focus-ring);outline-offset:-2px}.your-move__view[data-v-e3e1502a]{margin-left:10px}.your-move__next[data-v-e3e1502a]{font-weight:500}.your-move__quote[data-v-e3e1502a]{max-width:48ch;color:var(--md-on-surface-variant);font-size:12px;line-height:16px}.your-move__quote a[data-v-e3e1502a]{color:var(--md-on-surface-variant)}.your-move__problem[data-v-e3e1502a]{margin:4px 0 0;padding:4px 10px;font-size:12px}.your-move__mark[data-v-e3e1502a]{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 12px;border:1px solid var(--md-outline);border-radius:999px;color:var(--md-on-surface);font-size:12px;font-weight:600;white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none}.your-move__mark[data-v-e3e1502a]:hover{background:var(--state-hover)}.your-move__mark[data-v-e3e1502a]:before{content:"";width:9px;height:5px;border-bottom:2px solid currentcolor;border-left:2px solid currentcolor;transform:translateY(-2px) rotate(-45deg)}.your-move__mark--undo[data-v-e3e1502a]:before{display:none}.your-move__mark[data-v-e3e1502a]:focus-visible{outline:var(--focus-ring);outline-offset:2px}.your-move__row--handled td[data-v-e3e1502a]{opacity:.55}.your-move__row--handled .your-move__next[data-v-e3e1502a]{text-decoration:line-through;text-decoration-color:var(--md-outline)}.your-move__row--handled .table__actions[data-v-e3e1502a]{opacity:1}.todos__view[data-v-6f034e1e]{margin-left:10px}.todos__add[data-v-6f034e1e]{display:flex;align-items:center;gap:8px;padding:8px 0}.todos__input[data-v-6f034e1e]{flex:1;min-width:0;height:32px;padding:0 12px;background:rgb(var(--md-state),.04);font-size:14px}.todos__list[data-v-6f034e1e]{margin:0 -20px;padding:0;overflow-y:auto;list-style:none}.todos__item[data-v-6f034e1e]{display:grid;grid-template-columns:20px minmax(0,1fr) auto 20px;align-items:center;gap:10px;padding:7px 20px;border-top:1px solid var(--md-outline-variant)}.todos__item[data-v-6f034e1e]:first-child{border-top:0}.todos__item[data-v-6f034e1e]:hover{background:rgb(var(--md-state),.06)}.todos__text[data-v-6f034e1e]{overflow-wrap:anywhere}.todos__item--done .todos__text[data-v-6f034e1e]{color:var(--md-on-surface-variant);text-decoration:line-through}.todos__empty[data-v-6f034e1e]{display:block;padding:6px 20px 10px}.todos__check[data-v-6f034e1e]{box-sizing:border-box;display:grid;place-items:center;width:18px;height:18px;border:2px solid var(--md-outline);border-radius:5px;cursor:pointer}.todos__check--on[data-v-6f034e1e]{border-color:var(--md-primary);background:var(--md-primary)}.todos__check--on[data-v-6f034e1e]:after{content:"";width:8px;height:4px;border-bottom:2px solid var(--md-on-primary);border-left:2px solid var(--md-on-primary);transform:translateY(-1px) rotate(-45deg)}.todos__meta[data-v-6f034e1e]{color:var(--md-on-surface-variant);font-size:11px;white-space:nowrap}.todos__delete[data-v-6f034e1e]{color:var(--md-on-surface-variant);font-size:16px;line-height:1;text-align:center;cursor:pointer;opacity:0}.todos__delete[data-v-6f034e1e]:focus-visible{opacity:1}.todos__item:hover .todos__delete[data-v-6f034e1e]{opacity:1}@media(min-width:901px){.dashboard--fit .dashboard__todo .todos__list[data-v-6f034e1e]{flex:1 1 auto;min-height:0}.dashboard--fit .dashboard__todo .todos__list[data-v-6f034e1e]:not(:has(.todos__item)){display:none}}.waiting__table[data-v-a632447c]{table-layout:fixed}.waiting__col--ticket[data-v-a632447c]{width:22%}.waiting__col--status[data-v-a632447c],.waiting__col--with[data-v-a632447c]{width:6.5rem}.waiting__col--pr[data-v-a632447c]{width:4.5rem}.waiting__col--actions[data-v-a632447c]{width:2rem}.waiting__reason[data-v-a632447c]{display:block;min-width:0;max-width:44ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.feed-item[data-v-27e556e1]{display:grid;grid-template-columns:32px minmax(0,1fr);align-items:start;gap:12px;padding:10px 20px;cursor:default}.feed-item[data-v-27e556e1]:hover{background:var(--state-hover)}.feed-item.feed-item--open[data-v-27e556e1]{background:rgb(var(--md-state),.05)}.feed-item.feed-item--new[data-v-27e556e1]{background:var(--new-bg);box-shadow:inset 4px 0 0 var(--new),inset 0 0 0 1px var(--new-edge)}.feed-item__icon[data-v-27e556e1]{flex:none;display:grid;place-items:center;width:32px;height:32px;border-radius:50%;background:var(--md-s-highest);color:var(--md-on-surface-variant);font-size:11px;font-weight:700;letter-spacing:.02em}.feed-item__icon--jira[data-v-27e556e1]{background:var(--md-primary-container);color:var(--md-on-primary-container)}.feed-item__icon--bad[data-v-27e556e1]{background:var(--md-error-container);color:var(--md-on-error-container)}.feed-item__icon--ok[data-v-27e556e1]{background:var(--md-tertiary-container);color:var(--md-on-tertiary-container)}.feed-item__body[data-v-27e556e1]{display:grid;gap:2px;min-width:0}.feed-item__top[data-v-27e556e1]{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;font-size:14px}.feed-item__right[data-v-27e556e1]{flex:none;display:inline-flex;align-items:center;gap:4px;margin-left:auto}.feed-item__kind[data-v-27e556e1]{font-weight:500}.feed-item__kind--bad[data-v-27e556e1]{color:var(--md-error)}.feed-item__who[data-v-27e556e1]{color:var(--md-on-surface-variant)}.feed-item__text[data-v-27e556e1]{display:-webkit-box;overflow:hidden;color:var(--md-on-surface-variant);font-size:12px;line-height:16px;letter-spacing:.03em;overflow-wrap:anywhere;-webkit-line-clamp:2;-webkit-box-orient:vertical}.feed-item__toggle[data-v-27e556e1]{display:inline-grid;place-items:center;width:24px;height:24px;margin-left:2px;border-radius:50%;color:var(--md-on-surface-variant);font-size:0;line-height:1;cursor:pointer;-webkit-user-select:none;user-select:none;transition:transform .15s ease}.feed-item__toggle[data-v-27e556e1]:before{content:"";width:7px;height:7px;border-right:2px solid currentcolor;border-bottom:2px solid currentcolor;transform:translateY(-2px) rotate(45deg)}.feed-item__toggle[data-v-27e556e1]:hover{background:rgb(var(--md-state),.1)}.feed-item__toggle[aria-expanded=true][data-v-27e556e1]{transform:rotate(180deg)}.feed-item__toggle[data-v-27e556e1]:focus-visible{outline:var(--focus-ring)}.feed-item__full[data-v-27e556e1]{max-height:16rem;margin-top:6px;padding:10px 12px;overflow:auto;border:1px solid var(--glass-edge);border-radius:var(--md-r-chip);background:rgb(var(--glass),.55);color:var(--md-on-surface);font-size:13px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}.feed-item__actions[data-v-27e556e1]{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.feed-item--new .feed-item__kind[data-v-27e556e1],.feed-item--lit .feed-item__kind[data-v-27e556e1]{color:var(--md-on-surface);font-weight:700}.feed-item--new .feed-item__text[data-v-27e556e1],.feed-item--lit .feed-item__text[data-v-27e556e1]{color:var(--md-on-surface)}.feed-item--new .feed-item__icon[data-v-27e556e1],.feed-item--lit .feed-item__icon[data-v-27e556e1]{box-shadow:0 0 0 2px var(--new)}.feed-item--new .feed-item__when[data-v-27e556e1]:before,.feed-item--lit .feed-item__when[data-v-27e556e1]:before{content:"NEW";display:inline-block;margin-right:6px;padding:1px 6px;border-radius:4px;background:var(--new);color:var(--new-ink);font-size:10px;font-weight:800;letter-spacing:.06em;vertical-align:1px}.feed-item:not(.feed-item--new) .feed-item__body[data-v-27e556e1]{opacity:.78}.feed__bar[data-v-d92a7c74]{display:flex;flex:none;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:8px;margin:0 0 8px}.feed__new-count[data-v-d92a7c74]{display:inline-flex;align-items:center;height:22px;padding:0 9px;border-radius:999px;background:var(--new);color:var(--new-ink);box-shadow:0 0 12px var(--new-glow);font-size:12px;font-weight:800}.feed__mark-read[data-v-d92a7c74]{flex:none;padding:4px 8px;border-radius:999px;color:var(--md-primary);font-size:13px;font-weight:500;cursor:pointer;-webkit-user-select:none;user-select:none}.feed__mark-read[data-v-d92a7c74]:hover{background:var(--state-hover)}.feed__mark-read[data-v-d92a7c74]:focus-visible{outline:var(--focus-ring)}.feed__search[data-v-d92a7c74]{display:flex;flex:none;align-items:center;gap:8px;margin:0 0 8px}.feed__search-input[data-v-d92a7c74]{flex:1;min-width:0;height:30px;padding:0 10px;font-size:13px}.feed__search-count[data-v-d92a7c74]{flex:none;color:var(--md-on-surface-variant);font-size:12px}.feed__list[data-v-d92a7c74]{flex:1 1 auto;min-height:12rem;margin:0 -20px;padding:0 0 4px;overflow-y:auto;list-style:none}.feed__group[data-v-d92a7c74]{display:grid;grid-template-columns:minmax(0,1fr);align-items:start;gap:0;padding:0;cursor:default}.feed__group[data-v-d92a7c74]:hover{background:var(--state-hover)}.feed__group.feed__group--open[data-v-d92a7c74]{background:rgb(var(--md-state),.05)}.feed__group.feed__group--new[data-v-d92a7c74]{background:var(--new-bg);box-shadow:inset 4px 0 0 var(--new),inset 0 0 0 1px var(--new-edge)}.feed__group-head[data-v-d92a7c74]{display:grid;grid-template-columns:32px minmax(0,1fr) auto;align-items:center;gap:12px;padding:10px 20px;cursor:pointer}.feed__group-copy[data-v-d92a7c74]{opacity:.55}.feed__group-head:hover .feed__group-copy[data-v-d92a7c74]{opacity:1}.feed__group-head[data-v-d92a7c74]:hover{background:var(--state-hover)}.feed__group--new>.feed__group-head[data-v-d92a7c74]{background:var(--new-bg);box-shadow:inset 4px 0 0 var(--new)}.feed__group--move[data-v-d92a7c74],.feed__group--move>.feed__group-head[data-v-d92a7c74]{box-shadow:inset 4px 0 0 var(--move)}.feed__group.feed__group--new.feed__group--move[data-v-d92a7c74]{box-shadow:inset 4px 0 0 var(--move),inset 0 0 0 1px var(--new-edge)}.feed__group-text[data-v-d92a7c74]{min-width:0}.feed__group-key[data-v-d92a7c74]{display:flex;align-items:baseline;gap:8px;font-size:14px}.feed__group-ticket[data-v-d92a7c74]{flex:none;font-weight:600}.feed__group-sum[data-v-d92a7c74]{overflow:hidden;color:var(--md-on-surface-variant);font-size:13px;text-overflow:ellipsis;white-space:nowrap}.feed__group-meta[data-v-d92a7c74]{display:flex;align-items:center;gap:8px;margin-top:2px;color:var(--md-on-surface-variant);font-size:12px}.feed__group-kinds[data-v-d92a7c74]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.feed__group-when[data-v-d92a7c74]{margin-left:auto}.feed__group-count[data-v-d92a7c74]{flex:none;display:inline-flex;align-items:center;height:18px;padding:0 7px;border-radius:999px;background:var(--md-s-highest);color:var(--md-on-surface-variant);font-size:11px;font-weight:700}.feed__group-count--new[data-v-d92a7c74]{background:var(--new);color:var(--new-ink)}.feed__children[data-v-d92a7c74]{margin:0 0 0 35px;padding:0 0 4px;border-left:2px solid var(--md-outline-variant);list-style:none}.feed__children .feed-item[data-v-d92a7c74]{padding-left:14px}.feed__group-caret[data-v-d92a7c74]{flex:none;display:grid;place-items:center;width:32px;height:32px;border-radius:50%;background:var(--md-s-highest);color:var(--md-on-surface-variant);font-size:14px;transition:transform .15s ease}.feed__group-caret[data-v-d92a7c74]:focus-visible{outline:var(--focus-ring)}.feed__group--open .feed__group-caret[data-v-d92a7c74]{transform:rotate(90deg)}@media(min-width:901px){.dashboard--fit .feed__list[data-v-d92a7c74]::-webkit-scrollbar{width:8px;height:8px}.dashboard--fit .feed__list[data-v-d92a7c74]::-webkit-scrollbar-thumb{border-radius:4px;background:var(--md-outline-variant)}}@media(max-width:900px){.feed__list[data-v-d92a7c74]{max-height:28rem}}.first-run[data-v-ef746675]{display:grid;place-self:start center;gap:14px;width:min(640px,100%);padding:28px 32px}.first-run__title[data-v-ef746675]{margin:0;font-size:22px;font-weight:400;line-height:28px}.first-run__text[data-v-ef746675]{margin:0;color:var(--md-on-surface-variant)}.first-run__actions[data-v-ef746675]{display:flex;flex-wrap:wrap;gap:8px;margin-top:4px}.first-run__ai[data-v-ef746675]{font-size:13px}.markdown-view[data-v-dcf42e95]{padding:16px 20px 20px;overflow:auto;overflow-wrap:anywhere;font-size:14px;line-height:1.6}.markdown-view[data-v-dcf42e95] h4,.markdown-view[data-v-dcf42e95] h5{margin:16px 0 6px;font-weight:600}.markdown-view[data-v-dcf42e95] h4{font-size:16px}.markdown-view[data-v-dcf42e95] h5{font-size:14px}.markdown-view[data-v-dcf42e95] p{margin:0 0 10px}.markdown-view[data-v-dcf42e95] ul,.markdown-view[data-v-dcf42e95] ol{margin:0 0 10px;padding-left:22px}.markdown-view[data-v-dcf42e95] li{margin:2px 0}.markdown-view[data-v-dcf42e95] code{padding:1px 5px;border-radius:4px;background:rgb(var(--md-state),.1);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}.markdown-view[data-v-dcf42e95] pre{margin:0 0 12px;padding:12px 14px;overflow:auto;border-radius:8px;background:#00000047}.markdown-view[data-v-dcf42e95] pre code{padding:0;background:none;font-size:12.5px;line-height:1.5;white-space:pre}.markdown-view[data-v-dcf42e95] blockquote{margin:0 0 10px;padding:4px 12px;border-left:3px solid var(--md-primary);color:var(--md-on-surface-variant)}.markdown-view[data-v-dcf42e95] strong{font-weight:600}.markdown-view[data-v-dcf42e95] .markdown-view__mention{color:var(--md-primary);font-weight:500}.feedback-sheet__card[data-v-ec34adcc]{width:min(640px,100%)}.feedback-sheet__text[data-v-ec34adcc]{min-height:7rem;resize:vertical}.feedback-sheet__note[data-v-ec34adcc]{margin:0;color:var(--md-on-surface-variant);font-size:12px}.feedback-sheet__report[data-v-ec34adcc]{display:flex;align-items:baseline;gap:8px;padding:4px 0;border-top:1px solid var(--md-outline-variant);font-size:13px}.feedback-sheet__report-title[data-v-ec34adcc]{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.field-row[data-v-2ba0ba86]{display:grid;grid-template-columns:170px minmax(0,1fr);align-items:center;gap:4px 14px}.field-row__label[data-v-2ba0ba86]{color:var(--md-on-surface);font-size:13px}.field-row__hint[data-v-2ba0ba86]{grid-column:2;color:var(--md-on-surface-variant);font-size:11px}.field-row__check[data-v-2ba0ba86]{display:flex;align-items:center;gap:8px;color:var(--md-on-surface-variant);font-size:11px}.field-row__colour[data-v-2ba0ba86]{width:44px;height:30px;padding:2px}.field-row__inline[data-v-2ba0ba86]{display:flex;align-items:center;gap:8px}.settings-sheet__card[data-v-779d567d]{width:min(1040px,100%);height:min(760px,100%)}.settings-sheet__dirty[data-v-779d567d]{color:var(--md-error);font-size:12px;font-weight:600}.settings-sheet__confirm[data-v-779d567d]{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:12px 20px 0;padding:8px 12px;border-radius:10px;background:var(--md-warn-container);color:var(--md-on-warn-container);font-size:13px}.settings-sheet__confirm-text[data-v-779d567d]{flex:1}.settings-sheet__alert[data-v-779d567d]{margin:12px 20px 0}.settings-sheet__body[data-v-779d567d]{display:grid;flex:1;grid-template-columns:220px minmax(0,1fr);gap:0;min-height:0}.settings-sheet__list[data-v-779d567d]{display:flex;flex-direction:column;gap:4px;margin:0;padding:12px;overflow:auto;border-right:1px solid var(--md-outline-variant);list-style:none}.settings-sheet__workspace[data-v-779d567d]{box-sizing:border-box;display:block;width:100%;padding:8px 12px;overflow:hidden;border-radius:10px;font-size:14px;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.settings-sheet__workspace[data-v-779d567d]:hover{background:var(--state-hover)}.settings-sheet__workspace[aria-current=true][data-v-779d567d]{background:var(--md-secondary-container);color:var(--md-on-secondary-container)}.settings-sheet__workspace[data-v-779d567d]:focus-visible{outline:var(--focus-ring);outline-offset:-2px}.settings-sheet__workspace--add[data-v-779d567d]{color:var(--md-primary);font-weight:500}.settings-sheet__connector[data-v-779d567d]{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--md-outline-variant);border-radius:12px;background:rgb(var(--glass),.35)}.settings-sheet__connector-head[data-v-779d567d]{display:flex;align-items:center;gap:10px}.settings-sheet__connector-name[data-v-779d567d]{flex:1;font-weight:600}.settings-sheet__actions[data-v-779d567d]{display:flex;flex-wrap:wrap;align-items:center;gap:10px}.settings-sheet__rules[data-v-779d567d]{display:flex;flex-direction:column;gap:6px;margin:0;padding:0 0 0 28px;font-size:13px}.settings-sheet__rule[data-v-779d567d]{display:grid;grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:12px;padding:4px 0;border-bottom:1px solid var(--md-outline-variant)}.settings-sheet__rule-when[data-v-779d567d]{color:var(--md-on-surface-variant);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow-wrap:anywhere}.settings-sheet__rule-then[data-v-779d567d]{overflow-wrap:anywhere}.settings-sheet__rules-note[data-v-779d567d]{margin:0;color:var(--md-on-surface-variant)}.app__shell[data-v-f146b085]{box-sizing:border-box;display:flex;flex-direction:column;height:100%;padding:12px 12px 12px 72px}.app__shell>.dashboard[data-v-f146b085]{flex:1 1 auto;min-height:0}@media(min-width:901px){.app__shell>.dashboard.dashboard--fit[data-v-f146b085]{height:auto}}@media(max-width:900px){.app__shell[data-v-f146b085]{height:auto}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d as v,E as f,o as s,a as o,g as a,F as c,f as t,n as w,u as n,j as u,h,b as y,i as g,_ as k,H as x}from"./_plugin-vue_export-helper-DHr5RHTx.js";import{g as N,S as R,m as b,o as C,i as d}from"./bem-
|
|
1
|
+
import{d as v,E as f,o as s,a as o,g as a,F as c,f as t,n as w,u as n,j as u,h,b as y,i as g,_ as k,H as x}from"./_plugin-vue_export-helper-DHr5RHTx.js";import{g as N,S as R,m as b,o as C,i as d}from"./bem-COKTjsky.js";const E={class:"app report-page"},S={key:0,class:"empty"},T={class:"report-page__head"},V={class:"report-page__id"},B={class:"report-page__title"},L={class:"report-page__meta"},P={class:"report-page__section"},W={class:"report-page__text"},A={class:"report-page__section"},D={key:0,class:"report-page__unanswered"},F={key:1,class:"empty"},I={class:"report-page__answer-meta"},U={class:"report-page__text"},j=v({__name:"ReportPage",setup(H){const r=new URLSearchParams(location.search).get("id")||"",e=g(null),p=g(r?"Loading…":"No report id in the link.");return f(()=>{r&&(document.title=r+" · Your report · Whose Turn",N("/api/feedback?id="+encodeURIComponent(r)).then(l=>{e.value=l.report||null,p.value=l.report?"":"No report "+r+" was filed from this machine."}).catch(()=>{p.value=R+"; reload this page once it is back."}))}),(l,i)=>(s(),o("main",E,[e.value?(s(),o(c,{key:1},[t("header",T,[t("span",V,a(e.value.id),1),t("h1",B,a(e.value.title),1),t("span",{class:w(["pill",n(b)("pill",n(C)(e.value.outcome))])},a(e.value.state||"filed, not yet seen"),3)]),t("p",L,[u(a(e.value.kind||"report")+" · filed "+a(n(d)(e.value.filed_at)),1),e.value.changed_at?(s(),o(c,{key:0},[u(" · last change "+a(n(d)(e.value.changed_at)),1)],64)):h("",!0)]),t("section",P,[i[0]||(i[0]=t("h2",{class:"report-page__heading"},"What you reported",-1)),t("p",W,a(e.value.body||"(no text kept)"),1)]),t("section",A,[i[1]||(i[1]=t("h2",{class:"report-page__heading"},"Answers",-1)),e.value.unanswered?(s(),o("p",D," Closed without an answer. The maintainer owes you a reason; this page will show it once it is written. ")):e.value.answers.length?h("",!0):(s(),o("p",F,"No answer yet.")),(s(!0),o(c,null,y(e.value.answers,(_,m)=>(s(),o("article",{key:m,class:"report-page__answer"},[t("p",I,a(_.by||"maintainer")+" · "+a(n(d)(_.at)),1),t("p",U,a(_.text),1)]))),128))])],64)):(s(),o("p",S,a(p.value),1))]))}}),z=k(j,[["__scopeId","data-v-167de311"]]);x(z).mount("#app");
|
package/dist/index.html
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
<title>Whose Turn</title>
|
|
7
7
|
<script src="/vendor/popper.min.js"></script>
|
|
8
8
|
<script src="/vendor/tippy-bundle.umd.min.js"></script>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-CuyWocyb.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-DHr5RHTx.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/scheme-DJ3WNw60.js">
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/bem-
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/bem-COKTjsky.js">
|
|
13
13
|
<link rel="stylesheet" crossorigin href="/assets/_plugin-vue_export-helper-BKveAJb2.css">
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-p8D7awn0.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body class="page">
|
|
17
17
|
<div id="app" class="page__mount"></div>
|
package/dist/report.html
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Your report · Whose Turn</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/report-
|
|
7
|
+
<script type="module" crossorigin src="/assets/report-BtGnm9ax.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-DHr5RHTx.js">
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/assets/bem-
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/assets/bem-COKTjsky.js">
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/assets/_plugin-vue_export-helper-BKveAJb2.css">
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/report-xVOo7qip.css">
|
|
12
12
|
</head>
|
package/lib/api.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { createFeedback, isConfigured } from './feedback.mjs';
|
|
|
10
10
|
import { composeIssue, DEFAULT_COMPOSER } from './compose.mjs';
|
|
11
11
|
import { readSize, saveSize, checkMode, spawnOpener } from './window.mjs';
|
|
12
12
|
import { frameHandOff } from './handoff.mjs';
|
|
13
|
+
import { noUpdates } from './update.mjs';
|
|
13
14
|
|
|
14
15
|
const send = (response, code, body, contentType = 'application/json', cacheControl = 'no-store') => {
|
|
15
16
|
const data = Buffer.isBuffer(body) ? body : Buffer.from(contentType === 'application/json' ? JSON.stringify(body) : body);
|
|
@@ -88,7 +89,7 @@ const aggregateSummary = (workspaces) => {
|
|
|
88
89
|
|
|
89
90
|
const OPEN_COOLDOWN_MS = 5000;
|
|
90
91
|
|
|
91
|
-
export const startApi = async ({ port, appDir, registry, requestPoll, saveConfig, secrets, inbox, http, reports, dataDir, opener = spawnOpener(appDir) }) => {
|
|
92
|
+
export const startApi = async ({ port, appDir, registry, requestPoll, saveConfig, secrets, inbox, http, reports, dataDir, opener = spawnOpener(appDir), updater = noUpdates() }) => {
|
|
92
93
|
const windowFile = createStatic(join(appDir, 'dist'));
|
|
93
94
|
const feedbackCache = {};
|
|
94
95
|
const pick = (id) => {
|
|
@@ -119,11 +120,14 @@ export const startApi = async ({ port, appDir, registry, requestPoll, saveConfig
|
|
|
119
120
|
for (const workspace of registry.workspaces) {
|
|
120
121
|
write({ ws: workspace.id, version: workspace.store.state.version, polled_at: workspace.store.state.polled_at });
|
|
121
122
|
}
|
|
123
|
+
write({ update: updater.status() });
|
|
122
124
|
const unsubscribe = registry.subscribe(write);
|
|
125
|
+
const unwatch = updater.subscribe(write);
|
|
123
126
|
const keepAlive = setInterval(() => response.write(': keep-alive\n\n'), 25000);
|
|
124
127
|
response.on('close', () => {
|
|
125
128
|
clearInterval(keepAlive);
|
|
126
129
|
unsubscribe();
|
|
130
|
+
unwatch();
|
|
127
131
|
});
|
|
128
132
|
};
|
|
129
133
|
|
|
@@ -137,13 +141,13 @@ export const startApi = async ({ port, appDir, registry, requestPoll, saveConfig
|
|
|
137
141
|
}
|
|
138
142
|
if (url.pathname === '/api/state') {
|
|
139
143
|
if (!registry.workspaces.length) {
|
|
140
|
-
return send(response, 200, { workspaces: [], problems: registry.problems });
|
|
144
|
+
return send(response, 200, { workspaces: [], problems: registry.problems, update: updater.status() });
|
|
141
145
|
}
|
|
142
146
|
const workspace = pick(url.searchParams.get('ws'));
|
|
143
|
-
return send(response, 200, { ...workspace.snapshot(url.searchParams.get('full') === '1'), workspaces: registry.workspaces.map((item) => item.describe()), problems: registry.problems });
|
|
147
|
+
return send(response, 200, { ...workspace.snapshot(url.searchParams.get('full') === '1'), workspaces: registry.workspaces.map((item) => item.describe()), problems: registry.problems, update: updater.status() });
|
|
144
148
|
}
|
|
145
149
|
if (url.pathname === '/api/summary') {
|
|
146
|
-
return send(response, 200, { ...aggregateSummary(registry.workspaces), problems: registry.problems, handed: inbox.pending().length });
|
|
150
|
+
return send(response, 200, { ...aggregateSummary(registry.workspaces), problems: registry.problems, handed: inbox.pending().length, update: updater.status() });
|
|
147
151
|
}
|
|
148
152
|
if (url.pathname === '/api/config') {
|
|
149
153
|
const tables = Object.fromEntries(registry.workspaces.map((workspace) => [workspace.id, workspace.rulesTable]));
|
|
@@ -348,6 +352,9 @@ export const startApi = async ({ port, appDir, registry, requestPoll, saveConfig
|
|
|
348
352
|
opener({ mode, port });
|
|
349
353
|
return send(response, 200, { ok: true });
|
|
350
354
|
}
|
|
355
|
+
if (url.pathname === '/api/update') {
|
|
356
|
+
return send(response, 200, { update: updater.apply() });
|
|
357
|
+
}
|
|
351
358
|
if (url.pathname === '/api/refresh') {
|
|
352
359
|
requestPoll();
|
|
353
360
|
return send(response, 200, { ok: true });
|
package/lib/errors.mjs
CHANGED
|
@@ -14,6 +14,8 @@ const REASONS = {
|
|
|
14
14
|
UPSTREAM_FAILED: { code: 502, status: 'UNAVAILABLE', message: 'The tracker did not answer; try again in a minute.' },
|
|
15
15
|
RATE_LIMITED: { code: 429, status: 'RESOURCE_EXHAUSTED', message: 'The source is rate-limited; try again later.' },
|
|
16
16
|
OPEN_TOO_SOON: { code: 429, status: 'RESOURCE_EXHAUSTED', message: 'The window was just opened; wait a few seconds before opening it again.' },
|
|
17
|
+
UPDATE_IN_PROGRESS: { code: 409, status: 'ABORTED', message: 'An update is already installing; the server restarts when it is done.' },
|
|
18
|
+
UPDATE_UNAVAILABLE: { code: 412, status: 'FAILED_PRECONDITION', message: 'There is no update to apply: the server runs from a checkout or already runs the latest version.' },
|
|
17
19
|
FORBIDDEN_ORIGIN: { code: 403, status: 'PERMISSION_DENIED', message: 'Only the local window may call this API.' },
|
|
18
20
|
INTERNAL: { code: 500, status: 'INTERNAL', message: 'The local server failed on this request; the details are in its log.' },
|
|
19
21
|
};
|
package/lib/feedback.mjs
CHANGED
|
@@ -11,6 +11,7 @@ const KINDS = { bug: 'Bug', improvement: 'Improvement', idea: 'Feature' };
|
|
|
11
11
|
|
|
12
12
|
const LOOKUP = `
|
|
13
13
|
query Lookup($team: String!, $project: String!) {
|
|
14
|
+
viewer { id }
|
|
14
15
|
teams(filter: { key: { eq: $team } }) { nodes { id key labels { nodes { id name } } } }
|
|
15
16
|
projects(filter: { name: { eqIgnoreCase: $project } }) { nodes { id name } }
|
|
16
17
|
}`;
|
|
@@ -129,10 +130,12 @@ export const createFeedback = async ({ settings, secrets, http, cache, reports }
|
|
|
129
130
|
if (!team || !project) {
|
|
130
131
|
throw new ApiError('NOT_CONFIGURED', { detail: `Linear team ${settings.team} or project ${settings.project} not found` });
|
|
131
132
|
}
|
|
132
|
-
cache.lookup = { teamId: team.id, projectId: project.id, labels: Object.fromEntries(team.labels.nodes.map((label) => [label.name.toLowerCase(), label.id])) };
|
|
133
|
+
cache.lookup = { teamId: team.id, projectId: project.id, assigneeId: data.viewer?.id || '', labels: Object.fromEntries(team.labels.nodes.map((label) => [label.name.toLowerCase(), label.id])) };
|
|
133
134
|
}
|
|
134
135
|
const labelId = cache.lookup.labels[(KINDS[kind] || kind).toLowerCase()];
|
|
135
|
-
|
|
136
|
+
// The key's owner gets the issue, so it lands on their own dashboard; `feedback.assign: false` leaves it to triage.
|
|
137
|
+
const assigneeId = settings.assign === false ? '' : cache.lookup.assigneeId;
|
|
138
|
+
const input = { teamId: cache.lookup.teamId, projectId: cache.lookup.projectId, title: cleanTitle, description: describe({ kind, body, context, from }), ...(labelId ? { labelIds: [labelId] } : {}), ...(assigneeId ? { assigneeId } : {}) };
|
|
136
139
|
const data = await call(http, key, CREATE, { input });
|
|
137
140
|
if (!data.issueCreate?.success) {
|
|
138
141
|
throw new ApiError('UPSTREAM_FAILED');
|
package/lib/update.mjs
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/** Self-update of an installed package: a dist-tags check every ten minutes, one desktop notification per new version, and the one-click install and restart behind POST /api/update. ADR 0007, RMA-231. */
|
|
2
|
+
import { execFile, spawn as spawnChild } from 'node:child_process';
|
|
3
|
+
import { openSync, readFileSync, realpathSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { ApiError } from './errors.mjs';
|
|
6
|
+
import { readJson, writeJson } from './store.mjs';
|
|
7
|
+
|
|
8
|
+
export const PACKAGE = 'whose-turn';
|
|
9
|
+
export const AGENT_LABEL = 'local.whose-turn';
|
|
10
|
+
export const UPDATE_CHECK_MS = 10 * 60 * 1000;
|
|
11
|
+
const INSTALL_TIMEOUT_MS = 10 * 60 * 1000;
|
|
12
|
+
const VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
13
|
+
|
|
14
|
+
/** Where the BladeForge plugin installs the package: `<dataDir>/app/node_modules/whose-turn`. */
|
|
15
|
+
export const installedRoot = (dataDir) => join(dataDir, 'app', 'node_modules', PACKAGE);
|
|
16
|
+
|
|
17
|
+
/** True when the server runs from the plugin's install, so an update has somewhere to land; a checkout is never offered one. */
|
|
18
|
+
export const runsInstalled = (appDir, dataDir, real = realpathSync) => {
|
|
19
|
+
try {
|
|
20
|
+
return real(appDir) === real(installedRoot(dataDir));
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** The dist-tag every install follows: `latest`, which the release workflow moves on every publish, prereleases included (RMA-230). */
|
|
27
|
+
export const CHANNEL = 'latest';
|
|
28
|
+
|
|
29
|
+
/** The launch agent this process runs under, as the exit code that brings it back, or null when it runs on its own: launchd names its job in XPC_SERVICE_NAME and restarts on any exit (KeepAlive); the systemd unit shows in the cgroup and restarts only on a non-zero exit (Restart=on-failure). */
|
|
30
|
+
export const probeAgent = ({ platform = process.platform, env = process.env, cgroup = () => readFileSync('/proc/self/cgroup', 'utf8') } = {}) => {
|
|
31
|
+
if (platform === 'darwin') {
|
|
32
|
+
return env.XPC_SERVICE_NAME === AGENT_LABEL ? { exitCode: 0 } : null;
|
|
33
|
+
}
|
|
34
|
+
if (platform === 'linux') {
|
|
35
|
+
try {
|
|
36
|
+
return cgroup().includes(`/${PACKAGE}.service`) ? { exitCode: 75 } : null;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** Runs npm and resolves with '' when it succeeded, else with the last lines it printed; on Windows npm is a .cmd, which needs a shell, so every argument is quoted. */
|
|
45
|
+
export const runNpm = (npm, args, platform = process.platform) => new Promise((resolve) => {
|
|
46
|
+
const shell = platform === 'win32';
|
|
47
|
+
execFile(npm, shell ? args.map((arg) => `"${arg}"`) : args, { timeout: INSTALL_TIMEOUT_MS, maxBuffer: 4 * 1024 * 1024, shell }, (error, stdout, stderr) => {
|
|
48
|
+
if (!error) {
|
|
49
|
+
return resolve('');
|
|
50
|
+
}
|
|
51
|
+
const lines = String(stderr || stdout || error.message).split('\n').map((line) => line.trim()).filter(Boolean);
|
|
52
|
+
resolve(lines.slice(-3).join(' ').slice(0, 400) || 'npm failed');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/** The restart after an install: under a launch agent exit and let it bring the new copy back; without one close the port, start the new copy detached and exit. */
|
|
57
|
+
export const restartServer = ({ server, root, dataDir, agent = () => probeAgent(), spawn = spawnChild, exit = process.exit }) => {
|
|
58
|
+
const found = agent();
|
|
59
|
+
if (found) {
|
|
60
|
+
return exit(found.exitCode);
|
|
61
|
+
}
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
server.close(resolve);
|
|
64
|
+
server.closeAllConnections();
|
|
65
|
+
}).then(() => {
|
|
66
|
+
const log = openSync(join(dataDir, 'server.log'), 'a');
|
|
67
|
+
spawn(process.execPath, [join(root, 'server.mjs')], { cwd: root, env: process.env, detached: true, stdio: ['ignore', log, log] }).unref();
|
|
68
|
+
exit(0);
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** The updater the API reads and writes; `installed` false (a checkout) keeps it idle and offers nothing. */
|
|
73
|
+
export const createUpdater = ({ http, dataDir, version, installed, notify, registry = 'https://registry.npmjs.org', npm = 'npm', run = runNpm, restart }) => {
|
|
74
|
+
const listeners = new Set();
|
|
75
|
+
const memoFile = join(dataDir, 'update.json');
|
|
76
|
+
let latest = '';
|
|
77
|
+
let state = 'idle';
|
|
78
|
+
let reason = '';
|
|
79
|
+
|
|
80
|
+
const status = () => ({ current: version, latest, available: installed && Boolean(latest) && latest !== version, state, ...(state === 'failed' ? { reason } : {}) });
|
|
81
|
+
const announce = () => listeners.forEach((listener) => listener({ update: status() }));
|
|
82
|
+
const move = (next, why = '') => {
|
|
83
|
+
state = next;
|
|
84
|
+
reason = why;
|
|
85
|
+
announce();
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const check = async () => {
|
|
89
|
+
if (!installed) {
|
|
90
|
+
return status();
|
|
91
|
+
}
|
|
92
|
+
const found = await http.fetchJson(`${registry}/-/package/${PACKAGE}/dist-tags`).then((tags) => tags && tags[CHANNEL], () => '');
|
|
93
|
+
if (typeof found === 'string' && VERSION.test(found) && found !== latest) {
|
|
94
|
+
latest = found;
|
|
95
|
+
announce();
|
|
96
|
+
if (found !== version && readJson(memoFile, {}).notified !== found) {
|
|
97
|
+
writeJson(memoFile, { notified: found });
|
|
98
|
+
notify(`Whose Turn ${found} is out`, 'Update from the window or your next session.');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return status();
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const install = async () => {
|
|
105
|
+
const failure = await run(npm, ['install', '--prefix', join(dataDir, 'app'), '--no-audit', '--no-fund', '--no-save', `${PACKAGE}@${latest}`]);
|
|
106
|
+
if (failure) {
|
|
107
|
+
return move('failed', failure);
|
|
108
|
+
}
|
|
109
|
+
move('restarting');
|
|
110
|
+
await restart();
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const apply = () => {
|
|
114
|
+
if (state === 'installing' || state === 'restarting') {
|
|
115
|
+
throw new ApiError('UPDATE_IN_PROGRESS', { state });
|
|
116
|
+
}
|
|
117
|
+
if (!status().available) {
|
|
118
|
+
throw new ApiError('UPDATE_UNAVAILABLE', { current: version, latest });
|
|
119
|
+
}
|
|
120
|
+
move('installing');
|
|
121
|
+
install().catch((error) => move('failed', String(error?.message || error)));
|
|
122
|
+
return status();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const start = () => {
|
|
126
|
+
check();
|
|
127
|
+
setInterval(check, UPDATE_CHECK_MS).unref();
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return { status, check, apply, start, subscribe: (listener) => { listeners.add(listener); return () => listeners.delete(listener); } };
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/** An updater for a server with nothing to update, the API's default. */
|
|
134
|
+
export const noUpdates = () => createUpdater({ http: null, dataDir: '', version: '', installed: false, notify: () => {}, restart: async () => {} });
|
package/package.json
CHANGED
package/server.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/** Whose Turn server: loads the workspace config, polls every workspace on one loop with rate-limit backoff, checks data health on a timer, serves the window and the local API. */
|
|
3
|
-
import {
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
4
5
|
import { fileURLToPath } from 'node:url';
|
|
5
6
|
import { loadConfig, saveWorkspaces, normaliseWorkspaces } from './lib/config.mjs';
|
|
6
7
|
import { createHttpClient } from './lib/http-client.mjs';
|
|
@@ -11,6 +12,8 @@ import { createRegistry } from './lib/registry.mjs';
|
|
|
11
12
|
import { openInbox } from './lib/inbox.mjs';
|
|
12
13
|
import { openReports } from './lib/feedback.mjs';
|
|
13
14
|
import { startApi } from './lib/api.mjs';
|
|
15
|
+
import { notify } from './lib/notify.mjs';
|
|
16
|
+
import { createUpdater, restartServer, runsInstalled } from './lib/update.mjs';
|
|
14
17
|
|
|
15
18
|
const appDir = dirname(fileURLToPath(import.meta.url));
|
|
16
19
|
const http = createHttpClient();
|
|
@@ -18,6 +21,18 @@ const first = loadConfig();
|
|
|
18
21
|
const secrets = createSecrets(first.dataDir);
|
|
19
22
|
const inbox = openInbox(first.dataDir);
|
|
20
23
|
const reports = openReports(first.dataDir);
|
|
24
|
+
const { version } = JSON.parse(readFileSync(join(appDir, 'package.json'), 'utf8'));
|
|
25
|
+
let server;
|
|
26
|
+
const updater = createUpdater({
|
|
27
|
+
http,
|
|
28
|
+
dataDir: first.dataDir,
|
|
29
|
+
version,
|
|
30
|
+
installed: runsInstalled(appDir, first.dataDir),
|
|
31
|
+
notify,
|
|
32
|
+
registry: process.env.WHOSE_TURN_REGISTRY,
|
|
33
|
+
npm: process.env.WHOSE_TURN_NPM,
|
|
34
|
+
restart: () => restartServer({ server, root: appDir, dataDir: first.dataDir }),
|
|
35
|
+
});
|
|
21
36
|
let pollSeconds = first.pollSeconds;
|
|
22
37
|
/** @type {{ stale_minutes: number, failing_polls: number }} */
|
|
23
38
|
let healthLimits = /** @type {any} */ (first.health);
|
|
@@ -56,16 +71,17 @@ const saveConfig = async (rawWorkspaces) => {
|
|
|
56
71
|
};
|
|
57
72
|
|
|
58
73
|
try {
|
|
59
|
-
await startApi({ port: first.port, appDir, registry, requestPoll, saveConfig, secrets, inbox, http, reports, dataDir: first.dataDir });
|
|
74
|
+
server = await startApi({ port: first.port, appDir, registry, requestPoll, saveConfig, secrets, inbox, http, reports, dataDir: first.dataDir, updater });
|
|
60
75
|
} catch (error) {
|
|
61
76
|
console.error(error.message);
|
|
62
77
|
process.exit(1);
|
|
63
78
|
}
|
|
64
|
-
console.log(`whose-turn on http://127.0.0.1:${first.port} · data ${first.dataDir} · workspaces ${registry.workspaces.map((workspace) => workspace.id).join(', ') || 'none'}`);
|
|
79
|
+
console.log(`whose-turn ${version} on http://127.0.0.1:${first.port} · data ${first.dataDir} · workspaces ${registry.workspaces.map((workspace) => workspace.id).join(', ') || 'none'}`);
|
|
65
80
|
for (const problem of registry.problems) {
|
|
66
81
|
console.error(problem);
|
|
67
82
|
}
|
|
68
83
|
pollLoop();
|
|
84
|
+
updater.start();
|
|
69
85
|
|
|
70
86
|
const HEALTH_TICK = 30000;
|
|
71
87
|
let lastTick = Date.now();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const T=e=>fetch(e).then(t=>t.json(),()=>Promise.reject(new Error("unreachable"))),E=(e,t)=>fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t||{})}).then(n=>n.json(),()=>Promise.reject(new Error("unreachable"))),s=(e,t)=>(e.details||[]).find(n=>n["@type"]===t),S=e=>{var o,r;const t=s(e,"BadRequest");if(t&&t.field_violations)return t.field_violations.map(i=>i.description);const n=(r=(o=s(e,"ErrorInfo"))==null?void 0:o.metadata)==null?void 0:r.detail;return[n?String(n):e.message]},h=e=>{var t;return((t=s(e,"ErrorInfo"))==null?void 0:t.reason)||""},O=e=>fetch("/api/handoff?"+new URLSearchParams(e)).then(t=>t.ok?t.text():Promise.reject(new Error(String(t.status)))),_=e=>typeof e=="string"&&/^https:\/\//.test(e)?e:"",f="No workspace configured. Open Settings.",y=e=>e!=="",N=(e,t,n)=>e.some(o=>o.id===t&&o.open_moves!==n)?e.map(o=>o.id===t?{...o,open_moves:n}:o):e,A=(e,t)=>{const n=e.filter(a=>a.group==="you").map(a=>({row:a,handled:!!a.handled})).sort((a,c)=>a.row.rank-c.row.rank||String(a.row.updated).localeCompare(String(c.row.updated))),o=n.filter(a=>!a.handled).length,r=t==="handled";return{rows:n.filter(a=>a.handled===r),open:o,handled:n.length-o}},d=e=>String(e.status_since||e.updated),R=e=>e.filter(t=>t.group==="done").sort((t,n)=>d(n).localeCompare(d(t))).slice(0,15),b=e=>e.filter(t=>t.group==="others").sort((t,n)=>String(n.updated).localeCompare(String(t.updated))),v=e=>e==="failing"?"bad":e==="running"?"warn":e==="passing"?"ok":"",g={code_review:"info",qa:"info",on_hold:"warn"},P=e=>g[e||""]||"",D=e=>({open:"info",done:"ok",dropped:"warn"})[e||""]||"info",k=(e,t,n=1)=>e.filter(o=>o>t+n).length,p="unreachable",l="The local server is not answering",u=l+"; try again once it is back.",C=l+"; your text is kept.",m={NO_WORKSPACE:f,NOT_CONFIGURED:"The maintainer's tracker is not connected on this machine, so nothing can be sent from here.",UPSTREAM_FAILED:"The tracker did not answer; try again in a minute.",RATE_LIMITED:"The source is rate-limited; the monitor backs off.",OPEN_TOO_SOON:"The app window is already opening; give it a few seconds.",INTERNAL:"The local server failed; see its log."},w=(e,t="Not saved")=>m[h(e)]||t+": "+(e.message||"the server refused the change."),U=(e,{refused:t="Not saved",unreachable:n=u}={})=>{if(e instanceof Error)return e.message===p?n:t+": "+e.message;if(!e||typeof e!="object"||Array.isArray(e))return"";const o=e.error;return o?w(o,t):""},j=e=>{const t=Math.round((Date.now()-new Date(e).getTime())/6e4);return isFinite(t)?t<1?"now":t<60?t+"m ago":t<2880?Math.round(t/60)+"h ago":new Date(e).toLocaleDateString("en-US",{month:"short",day:"numeric"}):""},L=e=>{const t=(Date.now()-new Date(e??"").getTime())/36e5;return!isFinite(t)||t<0?{text:"",old:!1,hours:0}:{text:t<1?"<1h":t<48?Math.floor(t)+"h":Math.floor(t/24)+"d",old:t>=24,hours:t}},M=e=>e?new Date(e).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"}):"",I=(e,t)=>t?`${e}--${t}`:"";export{l as S,C as T,y as a,U as b,j as c,v as d,w as e,P as f,T as g,O as h,M as i,R as j,L as k,k as l,I as m,b as n,D as o,E as p,S as q,h as r,_ as s,N as w,A as y};
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{w as K,t as qe,r as Ye,s as Jt,c as w,d as N,o as r,a as i,F as V,b as j,e as X,u,n as W,f as o,g as _,h as T,i as I,_ as F,j as U,k as O,l as q,m as We,p as vt,q as Ne,v as te,x as Zt,y as Le,z as je,A as He,B as Qt,C as kt,D as es,E as ts,G as ss,H as ns}from"./_plugin-vue_export-helper-DHr5RHTx.js";import{s as os,m as yt,u as gt,a as wt,r as bt,p as as,b as ls,i as ot,l as xt,c as rs,M as G,d as we,e as ke,f as is,g as Xe,h as Me,j as cs,k as Ke,n as ds,o as us,q as ps,t as _s,v as at,w as hs}from"./scheme-DJ3WNw60.js";import{g as he,r as fs,a as ms,e as vs,w as $t,p as le,b as oe,T as Pe,S as xe,c as ne,h as ks,s as Ge,m as fe,d as ys,f as Ct,i as Je,j as gs,k as Ze,l as ws,y as bs,n as xs,o as $s,q as St}from"./bem-puYdOas_.js";const pe=os(bt),lt=(e,t)=>{const s=G,a=new Float32Array(s*s);for(let l=0;l<a.length;l+=1)a[l]=e[l]===t?1e6:0;const n=(l,c,h)=>{a[c]+h<a[l]&&(a[l]=a[c]+h)};for(let l=0;l<s;l+=1)for(let c=0;c<s;c+=1){const h=l*s+c;c>0&&n(h,h-1,1),l>0&&n(h,h-s,1),c>0&&l>0&&n(h,h-s-1,Math.SQRT2),c<s-1&&l>0&&n(h,h-s+1,Math.SQRT2)}for(let l=s-1;l>=0;l-=1)for(let c=s-1;c>=0;c-=1){const h=l*s+c;c<s-1&&n(h,h+1,1),l<s-1&&n(h,h+s,1),c<s-1&&l<s-1&&n(h,h+s+1,Math.SQRT2),c>0&&l<s-1&&n(h,h+s-1,Math.SQRT2)}return a},De=e=>{const t=String(e||"").trim(),s=/^#([0-9a-f]{6})$/i.exec(t);if(s)return[0,2,4].map(n=>parseInt(s[1].slice(n,n+2),16));const a=t.match(/[\d.]+/g);return a&&a.length>=3?a.slice(0,3).map(Number):null},Cs=e=>{const t=getComputedStyle(e),s=De(t.getPropertyValue("--md-primary"))||[44,86,151],a=De(t.getPropertyValue("--md-on-primary-container"))||s;return{core:ot()?s:s.map((n,l)=>xt(n,a[l],.35)),rim:De(t.getPropertyValue("--blob-3"))||[23,223,220],strength:ot()?.9:1}},Ss=(e,t,s)=>({size:e.range(.85,1.05)*Math.min(t,s),right:e.chance(.4),bottom:e.chance(.7),overflowX:e.range(-.02,.06),overflowY:e.range(-.02,.04)}),Ts=e=>{const t={minX:G,maxX:0,minY:G,maxY:0};for(let a=0;a<e.length;a+=1)if(e[a]){const n=a%G,l=Math.floor(a/G);t.minX=Math.min(t.minX,n),t.maxX=Math.max(t.maxX,n),t.minY=Math.min(t.minY,l),t.maxY=Math.max(t.maxY,l)}const s=a=>(a-G/2)/we;return{minX:s(t.minX),maxX:s(t.maxX),minY:s(t.minY),maxY:s(t.maxY)}},Ms=(e,t,s,a)=>{const n=t.maxX-t.minX,l=t.maxY-t.minY,c=Math.min(e.size,2*s*.95/n,2*a*.98/l),h=c/2,m=-e.overflowX*c,p=-e.overflowY*c;return{size:c,centerX:e.right?s-m-t.maxX*h:m-t.minX*h,centerY:e.bottom?a-p-t.maxY*h:p-t.minY*h}},As=(e,t,s)=>"rgba("+e.core.map((n,l)=>Math.round(xt(n,e.rim[l],t))).join(",")+","+s.toFixed(3)+")",Rs=(e,t,s)=>{const a=[],n=t.size/62,l=t.size/2/we,c=t.size*is/4,h=n*.866;for(let m=0,p=t.centerY-c;p<t.centerY+c;m+=1,p+=h)for(let y=t.centerX-c+m%2*n/2;y<t.centerX+c;y+=n){const A=y+e.range(-.3,.3)*n,E=p+e.range(-.3,.3)*n,R=Math.floor((A-t.centerX)/l+G/2),C=Math.floor((E-t.centerY)/l+G/2);if(R<0||C<0||R>=G||C>=G)continue;const d=C*G+R,v=e.next(),f=e.range(.75,1.25),B=e.range(-.15,.15);if(s.inside[d]){const z=s.depth[d]/we,L=ke(z/.035),J=ke(z/.3);if(v>.5+.5*L)continue;a.push({x:A,y:E,radius:n*.5*(.45+.55*L)*(.95+.3*J)*f,alpha:.5+.25*L+.25*J,tint:ke(.65-.65*J+B)})}else{const z=s.gap[d]/we;if(v>.14*Math.exp(-z/.05)+.004)continue;a.push({x:A,y:E,radius:n*e.range(.1,.24),alpha:.35,tint:ke(.8+B)})}}return a},Es=()=>document.getElementById("wt"),Os=()=>document.getElementById("backdrop"),Tt=()=>Math.floor(Math.random()*4294967296),ye={},D={workspace:"",seed:Tt(),species:"",wantedSpecies:"",wantedPose:"",built:null},Ds=(e,t)=>{const s=rs(D.seed),a=pe[Math.floor(s.next()*pe.length)];D.species=pe.includes(D.wantedSpecies)?D.wantedSpecies:a;const n=Ss(s,e,t),l={facing:n.right?-1:1,tilt:s.range(-.07,.07),stretchX:s.range(.92,1.08),stretchY:s.range(.92,1.08)};s.chance(.25)&&(l.facing=-l.facing);const c=as(ls(bt,D.species,s,{pose:D.wantedPose}).shapes,l),h={inside:c,depth:lt(c,1),gap:lt(c,0)},m=Ms(n,Ts(c),e,t);return{key:D.seed+":"+D.species+":"+e+"x"+t,dots:Rs(s,m,h)}},$e=()=>{const e=Os();if(!e)return;const t=e.clientWidth,s=e.clientHeight,a=window.devicePixelRatio||1,n=Math.round(t*a),l=Math.round(s*a);(e.width!==n||e.height!==l)&&(e.width=n,e.height=l);const c=e.getContext("2d");if(!c||(c.setTransform(a,0,0,a,0,0),c.clearRect(0,0,t,s),!t||!s))return;const h=D.seed+":"+(D.wantedSpecies||"?")+":"+t+"x"+s;(!D.built||D.built.key.split(":")[0]!==String(D.seed)||!D.built.key.endsWith(t+"x"+s)||D.wantedSpecies&&D.species!==D.wantedSpecies)&&(D.built=Ds(t,s));const m=Es();if(!m)return h;const p=Cs(m);return D.built.dots.forEach(y=>{c.fillStyle=As(p,y.tint,y.alpha*p.strength),c.beginPath(),c.arc(y.x,y.y,y.radius,0,Math.PI*2),c.fill()}),h};window.whoseTurnBackdrop={species:pe.slice(),seeded:yt,get current(){return{workspace:D.workspace,species:D.species,seed:D.seed}},show:e=>{const t=e||{},s=String(t.workspace||"");ye[s]||(ye[s]=Tt());const a=t.seed!==void 0?Number(t.seed)>>>0:ye[s];ye[s]=a;const n=t.species&&pe.includes(t.species)?t.species:"",l=String(t.pose||"");return s===D.workspace&&a===D.seed&&n===D.wantedSpecies&&l===D.wantedPose&&D.built||(D.workspace=s,D.seed=a,D.wantedSpecies=n,D.wantedPose=l,D.built=null,$e()),window.whoseTurnBackdrop.current},repaint:$e};K(Xe,$e);gt(window,"resize",wt($e,200));const{show:Mt,repaint:Is,seeded:jr}=window.whoseTurnBackdrop,Bs=()=>{window.tippy&&window.tippy.delegate(document.body,{target:"[data-tip]",content:e=>e.dataset.tip||"",trigger:"mouseenter focus click",delay:[0,80],duration:[120,80],interactive:!0,maxWidth:440,placement:"top",theme:"wt",appendTo:()=>document.body})},Vs=()=>{let e=0;return{next:()=>++e,accepts:t=>t===e}},rt=e=>{const t=structuredClone(qe(e)),s=t.connectors||{},a=t.rules||{};return a.statuses=a.statuses||{},{...t,connectors:Array.isArray(s)?s:Object.entries(s).map(([n,l])=>({type:n,...l})),rules:a,theme:t.theme||{}}},Ws=e=>{const t=structuredClone(qe(e));return t.theme&&!Object.keys(t.theme).length&&delete t.theme,t.rules&&!Object.keys(t.rules.statuses||{}).length&&delete t.rules.statuses,t.rules&&!Object.keys(t.rules).length&&delete t.rules,t},it=new WeakMap;let Ns=0;const ct=e=>{const t=qe(e);let s=it.get(t);return s||(s=++Ns,it.set(t,s)),s},dt=e=>JSON.stringify(e),js=(e,t)=>{const s=[];return e.forEach(a=>a.connectors.forEach(n=>{const l=t.get(n)||{};Object.keys(l).forEach(c=>{l[c]&&s.push({ws:a.id,connector:n.id||n.type,key:c,value:l[c]})})})),s},re=(e,t,s)=>{typeof s=="boolean"||s&&s.length?e[t]=s:delete e[t]},Ps=(e,t)=>{const s=a=>{const n=t.indexOf(a.id);return n<0?t.length:n};return[...e].sort((a,n)=>s(a)-s(n))},Us=(e,t,s,a)=>{const n=e.filter(l=>l!==t);return n.splice(n.indexOf(s)+(a?0:1),0,t),n},Fs={gt:">",gte:"≥",lt:"<",lte:"≤",prefix:"starts with"},zs=(e,t)=>`${e} is ${t?"present":"absent"}`,qs=(e,t)=>Array.isArray(t)?`${e} in ${t.join(", ")}`:t&&typeof t=="object"?Object.entries(t).map(([s,a])=>s==="present"?zs(e,a):`${e} ${Fs[s]} ${JSON.stringify(a)}`).join(" and "):`${e} = ${t}`,Ys=e=>({when:Object.entries(e.when).map(([t,s])=>qs(t,s)).join(" and ")||"anything else",then:e.then.whose==="hidden"?"hidden":`${e.then.whose}: ${e.then.next}`}),Ls=e=>e.modal?"modal":e.settings?"settings":e.feedback?"feedback":null,Hs=(e,t)=>{const s=new URLSearchParams(e).get("mode");return s==="app"||s==="tab"?s:t==="app"?"app":"tab"},Xs=e=>"/?mode=tab"+(e?"&ws="+encodeURIComponent(e):""),Ks=e=>({width:e.outerWidth,height:e.outerHeight}),At={writeDefaults:!1},Rt=Me("wt-ws","",At),Et=cs("wt-mode","",At),k=Jt({workspaces:[],workspace:{id:"",name:"",connectors:[],links:[],key_pattern:"",theme:null},turn:[],feed:[],marks:{seen:[],base:0,handled:{}},counts:{mine:0,others:0,failing:0,done:0},todos:[],errors:{},health:{ok:!0,stale:!1,fresh_at:"",failing:[],alerts:[]},problems:[],polled_at:""}),g=Ye({ws:new URLSearchParams(location.search).get("ws")||Rt.value,open:new Set,mode:Hs(location.search,Et.value),live:!1,reconnects:0,status:"Connecting…",modal:null,feedback:!1,settings:!1,settingsDirty:!1,settingsAsk:!1,settingsAt:""});Et.value=g.mode;const Ce=Ke("",6e3),ae=(e,t)=>{const s=oe(e,t);return s&&(Ce.value=s),!s},Gs=()=>{g.mode==="app"&>(window,"resize",wt(()=>le("/api/window",Ks(window)).catch(ae),600))},Ot={resume:()=>{}},ce=us({scheduler:e=>{const t=ps(e,3e4,{immediate:!1});return Ot.resume=t.resume,t}}),ut=w(()=>{const e=k.workspace.theme;return e&&e.light&&e.dark?e[Xe.value?"dark":"light"]:{}}),H=w(()=>k.counts),pt=w(()=>[...k.problems||[],...k.health.alerts.map(e=>e.text),...Object.entries(k.errors||{}).map(([e,t])=>e+": "+t)]),Js=(e="")=>{g.settingsAt=e,g.settings=!0},Dt=w(()=>Ls(g)),Zs=()=>{const e=Dt.value;if(e==="modal"&&(g.modal=null),e==="feedback"&&(g.feedback=!1),e==="settings"){if(g.settingsDirty){g.settingsAsk=!0;return}g.settings=!1}},Ue=new Map,Qs=(e,t)=>{t&&Ue.set(e,document.activeElement)},en=(e,t)=>{if(t)return;const s=Ue.get(e);Ue.delete(e),s instanceof HTMLElement&&s.focus()},tn={modal:()=>!!g.modal,settings:()=>g.settings,feedback:()=>g.feedback};Object.entries(tn).forEach(([e,t])=>{K(t,s=>Qs(e,s),{flush:"sync"}),K(t,s=>en(e,s),{flush:"post"})});let Se=-1,Fe=0;const _t=Vs(),me=(e=g.ws)=>{const t=_t.next();return he("/api/state"+(e?"?ws="+encodeURIComponent(e):"")).then(s=>_t.accepts(t)?s.error?fs(s.error)!=="NO_SUCH_WORKSPACE"||!ms(e)?(g.status=vs(s.error,"Could not load"),k):me(""):(Object.assign(k,s),g.ws=k.workspace.id,g.status="",Mt({workspace:k.workspace.id}),document.title="Whose Turn · "+k.workspace.name,k):k)},It=()=>(Se=-1,me()),_e=ds("/api/stream",[],{immediate:!1,autoConnect:!1,autoReconnect:!0,serializer:{read:e=>JSON.parse(e||"null")}});K(_e.status,e=>{g.live=e==="OPEN",g.live&&(g.status="",g.reconnects++)},{flush:"sync"});K(_e.error,e=>{e&&(g.status="Disconnected from the local server, retrying…")},{flush:"sync"});K(_e.data,e=>{e&&(typeof e.open_moves=="number"&&(k.workspaces=$t(k.workspaces,e.ws,e.open_moves)),!(k.workspace.id&&e.ws!==k.workspace.id)&&(g.status="",e.version!==Se?(Se=e.version,Fe===0&&me()):k.polled_at=e.polled_at))},{flush:"sync"});const sn=()=>{_e.status.value==="CLOSED"&&_e.open()},nn=()=>(Ot.resume(),me().then(sn)),Bt=e=>e===k.workspace.id?Promise.resolve():(Se=-1,Fe++,me(e).then(()=>{g.ws===e&&(Rt.value=g.ws,g.open.clear())},()=>{Ce.value=xe+"; staying on "+k.workspace.name+"."}).finally(()=>{Fe--})),Vt=(e,t)=>le(e,{ws:k.workspace.id,...t}),ze=e=>Vt("/api/marks",e).then(t=>{const s=oe(t);return s||(Object.assign(k,{marks:t.marks,turn:t.turn,counts:t.counts}),k.workspaces=$t(k.workspaces,k.workspace.id,t.counts.mine)),s},t=>oe(t)),on=e=>Vt("/api/todos",e).then(t=>{const s=oe(t,{unreachable:Pe});return!s&&Array.isArray(t)&&(k.todos=t),s},t=>oe(t,{unreachable:Pe})),an=e=>he("/api/config").then(t=>le("/api/config",{workspaces:Ps(t.workspaces,e),secrets:[]})).then(t=>{ae(t,{refused:"The order was not saved"})&&It()},t=>ae(t,{refused:"The order was not saved"})),Ie={id:"demo",name:"Demo",key_pattern:"\\bNW-\\d+\\b",theme:{seed:"#3d7a5c",accent:"#c27a2c"},connectors:[{type:"demo",scenario:"northwind",label:"Demo tracker"}]},ln=()=>he("/api/config").then(e=>e.workspaces.some(t=>t.id===Ie.id)?null:le("/api/config",{workspaces:[...e.workspaces,Ie],secrets:[]})).then(e=>{if(ae(e,{refused:"The demo workspace was not added"}))return Bt(Ie.id)},e=>ae(e,{refused:"The demo workspace was not added"})),ht=()=>{g.status="Checking now…",le("/api/refresh").catch(e=>{g.status="",ae(e)}),Mt({workspace:k.workspace.id,seed:Math.floor(Math.random()*4294967296)})},rn="M12 2.5l1.9 5.6 5.6 1.9-5.6 1.9L12 17.5l-1.9-5.6L4.5 10l5.6-1.9L12 2.5zM5 15l.9 2.6 2.6.9-2.6.9L5 22l-.9-2.6-2.6-.9 2.6-.9L5 15zm14-2l.7 2 2 .7-2 .7-.7 2-.7-2-2-.7 2-.7.7-2z",cn="M19.4 13a7.6 7.6 0 0 0 0-2l2-1.5-2-3.4-2.4 1a7.5 7.5 0 0 0-1.7-1L15 3.5H9l-.3 2.6a7.5 7.5 0 0 0-1.7 1l-2.4-1-2 3.4 2 1.5a7.6 7.6 0 0 0 0 2l-2 1.5 2 3.4 2.4-1a7.5 7.5 0 0 0 1.7 1l.3 2.6h6l.3-2.6a7.5 7.5 0 0 0 1.7-1l2.4 1 2-3.4-2-1.5zM12 15.5a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7z",dn="M19 8h-1.8a5.9 5.9 0 0 0-1.3-1.9l1.6-1.6-1.4-1.4-1.9 1.9A5.7 5.7 0 0 0 12 4.5a5.7 5.7 0 0 0-2.2.5L7.9 3.1 6.5 4.5l1.6 1.6A5.9 5.9 0 0 0 6.8 8H5v2h1.2a7 7 0 0 0 0 1v1H5v2h1.3a6 6 0 0 0 11.4 0H19v-2h-1.2v-1a7 7 0 0 0 0-1H19V8zm-7 10a4 4 0 0 1-4-4v-3a4 4 0 0 1 8 0v3a4 4 0 0 1-4 4zm-1-7h2v5h-2z",un=e=>{let t=2166136261;for(let s=0;s<e.length;s+=1)t^=e.charCodeAt(s),t=Math.imul(t,16777619);return t>>>0},Wt=e=>{const t=String(e||"").trim().split(/[\s_-]+/).filter(Boolean);return(t.length>=2?t.slice(0,3).map(a=>a.charAt(0)):(t[0]||"?").slice(0,2).split("")).join("").toUpperCase()},pn=e=>({1:17,2:14,3:11})[e.length]||11,_n=(e,t)=>{const s=t(un(e.id)),a=[],n=3+Math.floor(s()*3);for(let c=0;c<n;c+=1){const h=s()*Math.PI*2,m=9+s()*7;a.push({cx:(18+Math.cos(h)*m).toFixed(1),cy:(18+Math.sin(h)*m).toFixed(1),r:(1.6+s()*2.2).toFixed(1),opacity:(.35+s()*.4).toFixed(2)})}const l=Wt(e.name||e.id);return{dots:a,glyph:l,fontSize:pn(l)}},hn={class:"rail","aria-label":"Workspaces"},fn=["data-drop","href","aria-label","aria-selected","onClick","onDragstart","onDragover","onDrop"],mn={viewBox:"0 0 36 36",class:"rail__icon","aria-hidden":"true"},vn=["fill"],kn=["cx","cy","r","fill","opacity"],yn=["y","font-size","fill"],gn={class:"rail__label"},wn={key:0,class:"rail__badge"},bn={viewBox:"0 0 24 24",class:"rail__gear","aria-hidden":"true"},xn=["d"],$n=N({__name:"Rail",emits:["settings"],setup(e,{emit:t}){const s=t,a=w(()=>Object.fromEntries(k.workspaces.map(d=>[d.id,_n(d,yt)]))),n=w(()=>Object.fromEntries(k.workspaces.map(d=>{const v=d.theme?d.theme[Xe.value?"dark":"light"]:null;return[d.id,{fill:v?v["--md-primary-container"]:"var(--md-primary-container)",ink:v?v["--md-on-primary-container"]:"var(--md-on-primary-container)",dot:v?v["--md-primary"]:"var(--md-primary)"}]}))),l=d=>{d.detail>0&&d.currentTarget.blur()},c=(d,v)=>{l(d),Bt(v)},h=d=>{s("settings"),l(d)},m=I(""),p=I({}),y=d=>{const v=d.currentTarget;return d.clientY-v.getBoundingClientRect().top<v.offsetHeight/2},A=(d,v)=>{m.value=v.id,d.dataTransfer&&(d.dataTransfer.effectAllowed="move",d.dataTransfer.setData("text/plain",v.id))},E=()=>{m.value="",p.value={}},R=(d,v)=>{if(!m.value||m.value===v.id)return;d.preventDefault(),d.dataTransfer&&(d.dataTransfer.dropEffect="move");const f=y(d)?"before":"after";(p.value.id!==v.id||p.value.side!==f)&&(p.value={id:v.id,side:f})},C=(d,v)=>{if(!m.value||m.value===v.id)return;d.preventDefault();const f=Us(k.workspaces.map(B=>B.id),m.value,v.id,y(d));p.value={},an(f)};return(d,v)=>(r(),i("nav",hn,[(r(!0),i(V,null,j(u(k).workspaces,f=>(r(),i("a",{key:f.id,class:W(["rail__item",{"rail__item--dragging":m.value===f.id}]),"data-drop":p.value.id===f.id?p.value.side:null,href:"?ws="+encodeURIComponent(f.id),"aria-label":f.name,"aria-selected":f.id===u(k).workspace.id,draggable:"true",onClick:X(B=>c(B,f.id),["prevent"]),onDragstart:B=>A(B,f),onDragend:E,onDragover:B=>R(B,f),onDragleave:v[0]||(v[0]=B=>p.value={}),onDrop:B=>C(B,f)},[(r(),i("svg",mn,[o("rect",{x:"0",y:"0",width:"36",height:"36",rx:"10",fill:n.value[f.id].fill},null,8,vn),(r(!0),i(V,null,j(a.value[f.id].dots,(B,z)=>(r(),i("circle",{key:z,cx:B.cx,cy:B.cy,r:B.r,fill:n.value[f.id].dot,opacity:B.opacity},null,8,kn))),128)),o("text",{x:"18",y:a.value[f.id].fontSize>=17?24:23,"text-anchor":"middle","font-size":a.value[f.id].fontSize,"font-weight":"700","letter-spacing":"-0.5","font-family":"Roboto, Inter, system-ui, sans-serif",fill:n.value[f.id].ink},_(a.value[f.id].glyph),9,yn)])),o("span",gn,_(f.name),1),f.open_moves?(r(),i("span",wn,_(f.open_moves),1)):T("",!0)],42,fn))),128)),v[2]||(v[2]=o("span",{class:"rail__spacer"},null,-1)),o("a",{class:"rail__item",href:"#settings","aria-label":"Settings",onClick:X(h,["prevent"])},[(r(),i("svg",bn,[o("path",{d:u(cn),fill:"currentColor"},null,8,xn)])),v[1]||(v[1]=o("span",{class:"rail__label"},"Settings",-1))])]))}}),Cn=F($n,[["__scopeId","data-v-be863916"]]),Be=" · checks every minute",Nt=(e,t,s,a)=>{const n=e.failing.map(c=>c.source),l=t?ne(t):"—";return n.length&&n.length>=s?{dot:"off",text:"No data: "+n.join(", ")+" failing · last data "+ne(e.fresh_at),failing:n,noData:!0}:e.stale?{dot:"off",text:"Stale · last data "+ne(e.fresh_at)+Be,failing:n,noData:!1}:n.length?{dot:a?"warn":"off",text:n.join(", ")+" failing · last check "+l+Be,failing:n,noData:!1}:{dot:a?"ok":"off",text:"Live · last check "+l+Be,failing:n,noData:!1}},Sn=["data-tip"],Tn={class:"ai-button__icon",viewBox:"0 0 24 24","aria-hidden":"true"},Mn=["d"],An=N({__name:"AiButton",props:{query:{},big:{type:Boolean}},setup(e){const t=e,s={"":"Copy for AI: this with its context as text, ready to paste into Claude or any other assistant",sent:"Copied",failed:"Could not copy"},a=Ke("",1600),n=w(()=>s[a.value]),l=()=>ks(t.query).then(c=>navigator.clipboard.writeText(c)).then(()=>{a.value="sent"},()=>{a.value="failed"});return(c,h)=>(r(),i("button",{type:"button",class:W(["ai-button",{"ai-button--big":e.big,"ai-button--sent":u(a)==="sent","ai-button--failed":u(a)==="failed"}]),"aria-label":"Copy for AI","data-tip":n.value,onClick:X(l,["stop"])},[(r(),i("svg",Tn,[o("path",{d:u(rn),fill:"currentColor"},null,8,Mn)]))],10,Sn))}}),de=F(An,[["__scopeId","data-v-d32a60c7"]]),Rn={class:"status-bar"},En={class:"status-bar__icon",viewBox:"0 0 24 24","aria-hidden":"true"},On=["d"],Dn={key:0,class:"status-bar__notice",role:"alert"},In=["href"],Bn=["aria-disabled"],Vn=5e3,Wn=N({__name:"StatusBar",emits:["report"],setup(e,{emit:t}){const s=t,a=w(()=>(ce.value,Nt(k.health,k.polled_at,k.workspace.connectors.length,g.live))),n=I(!1),l=I(!1),c=w(()=>l.value?"Opened in the app window. You can close this tab.":g.status||a.value.text),h=p=>ae(p,{refused:"The app window did not open"}),m=()=>{if(n.value)return;n.value=!0,setTimeout(()=>n.value=!1,Vn);const p=y=>{h(y),l.value=!oe(y)};le("/api/open",{mode:"app"}).then(p,p)};return(p,y)=>(r(),i("div",Rn,[o("button",{type:"button",class:"btn status-bar__btn status-bar__report",title:"A bug or a wish about this monitor, straight to its backlog",onClick:y[0]||(y[0]=A=>s("report"))},[(r(),i("svg",En,[o("path",{d:u(dn),fill:"currentColor"},null,8,On)])),y[2]||(y[2]=U("Report ",-1))]),o("span",{class:W(["status-bar__live",{"status-bar__live--off":a.value.dot==="off","status-bar__live--warn":a.value.dot==="warn"}])},null,2),o("span",null,_(c.value),1),u(Ce)?(r(),i("span",Dn,_(u(Ce)),1)):T("",!0),o("button",{type:"button",class:"btn status-bar__btn",onClick:y[1]||(y[1]=(...A)=>u(ht)&&u(ht)(...A))},"Refresh now"),u(g).mode==="app"?(r(),i("a",{key:1,class:"btn status-bar__btn",href:u(Xs)(u(g).ws),target:"_blank",rel:"noopener"},"Open in browser",8,In)):(r(),i("button",{key:2,type:"button",class:W(["btn status-bar__btn",{"status-bar__btn--busy":n.value}]),"aria-disabled":n.value,onClick:m},_(n.value?"Opening…":"Open as app"),11,Bn)),O(de,{query:{all:"1"}}),y[3]||(y[3]=o("span",{class:"ai-hint status-bar__ai-hint"},"AI: copy everything",-1))]))}}),Nn=F(Wn,[["__scopeId","data-v-b74e71cb"]]),jt=e=>{const t=Me("wt-collapsed",[],{writeDefaults:!1}),s=w(()=>t.value.includes(e));return{collapsed:s,toggle:()=>{t.value=s.value?t.value.filter(n=>n!==e):[...t.value,e]}}},jn=["href"],Pn={key:1},se=N({__name:"ExtLink",props:{text:{default:""},url:{default:""}},setup(e){const t=e,s=w(()=>Ge(t.url));return(a,n)=>s.value?(r(),i("a",{key:0,href:s.value,target:"_blank",rel:"noopener"},_(e.text),9,jn)):(r(),i("span",Pn,_(e.text),1))}}),Un={class:"workspace-header__title"},Fn={class:"workspace-header__name"},zn={class:"workspace-header__line workspace-header__sentence"},qn={class:"workspace-header__count"},Yn={class:"workspace-header__count"},Ln={key:0},Hn={class:"workspace-header__count"},Xn={key:0,class:"workspace-header__line workspace-header__strip"},Kn={class:"workspace-header__count"},Gn={class:"workspace-header__count"},Jn={class:"workspace-header__count"},Zn={class:"workspace-header__count"},Qn={class:"workspace-header__line workspace-header__give"},eo={class:"workspace-header__links"},to={class:"workspace-header__tiles"},so={class:"workspace-header__tile workspace-header__tile--you"},no={class:"workspace-header__tile-value"},oo={class:"workspace-header__tile"},ao={class:"workspace-header__tile-value"},lo={class:"workspace-header__tile-value"},ro={class:"workspace-header__tile"},io={class:"workspace-header__tile-value"},co=N({__name:"WorkspaceHeader",setup(e){const{collapsed:t,toggle:s}=jt("header");return(a,n)=>(r(),i("header",{id:"summary",class:W(["island workspace-header",{"workspace-header--collapsed":u(t)}])},[o("div",Un,[o("h1",Fn,_(u(k).workspace.name||"Whose Turn"),1),o("p",zn,[o("b",qn,_(u(H).mine),1),n[3]||(n[3]=U(" waiting on you, ",-1)),o("b",Yn,_(u(H).others),1),n[4]||(n[4]=U(" waiting on someone else",-1)),u(H).failing?(r(),i("span",Ln,[n[1]||(n[1]=U(", ",-1)),o("b",Hn,_(u(H).failing),1),n[2]||(n[2]=U(" failing",-1))])):T("",!0),n[5]||(n[5]=U(". ",-1))]),u(t)?(r(),i("p",Xn,[n[6]||(n[6]=U(" Your move ",-1)),o("b",Kn,_(u(H).mine),1),n[7]||(n[7]=U(" · Waiting on others ",-1)),o("b",Gn,_(u(H).others),1),n[8]||(n[8]=U(" · Failing ",-1)),o("b",Jn,_(u(H).failing),1),n[9]||(n[9]=U(" · Done ",-1)),o("b",Zn,_(u(H).done),1)])):T("",!0),o("p",Qn,[O(de,{query:{ws:u(k).workspace.id,kind:"workspace"},big:""},null,8,["query"]),n[10]||(n[10]=o("span",{class:"ai-hint"},"AI: copy this workspace",-1)),o("span",eo,[(r(!0),i(V,null,j(u(k).workspace.links||[],l=>(r(),q(se,{key:l.url,text:l.label,url:l.url,title:l.url},null,8,["text","url","title"]))),128))])])]),o("button",{type:"button",class:W(["fold-toggle workspace-header__fold",{"fold-toggle--folded":u(t)}]),"aria-label":"Collapse or expand header","data-tip":"Fold the header; it stays folded until you open it again",onClick:n[0]||(n[0]=X((...l)=>u(s)&&u(s)(...l),["stop"]))}," ▾ ",2),o("div",to,[o("div",so,[n[11]||(n[11]=o("span",{class:"workspace-header__tile-label"},"Your move",-1)),o("strong",no,_(u(H).mine),1)]),o("div",oo,[n[12]||(n[12]=o("span",{class:"workspace-header__tile-label"},"Waiting on others",-1)),o("strong",ao,_(u(H).others),1)]),o("div",{class:W(["workspace-header__tile",{"workspace-header__tile--fail":u(H).failing}])},[n[13]||(n[13]=o("span",{class:"workspace-header__tile-label"},"Open PRs with failing checks",-1)),o("strong",lo,_(u(H).failing),1)],2),o("div",ro,[n[14]||(n[14]=o("span",{class:"workspace-header__tile-label"},"Done in the last 30 days",-1)),o("strong",io,_(u(H).done),1)])])],2))}}),uo=F(co,[["__scopeId","data-v-0f4b6d6e"]]),Qe=e=>{K(()=>g.reconnects,e)},po=["id"],_o={class:"island__head"},ho={class:"island__title"},fo={class:"island__note"},mo={class:"island__note-text"},vo=["data-tip"],ko=["aria-label"],yo={class:"island__body"},go=N({__name:"Island",props:{id:{},title:{},note:{default:""},tip:{default:""},column:{type:Boolean}},setup(e){const t=e,{collapsed:s,toggle:a}=jt(t.id);return(n,l)=>(r(),i("section",{id:e.id,class:W(["island",{"island--collapsed":u(s),"island--column":e.column}])},[o("div",_o,[o("h2",ho,[U(_(e.title)+" ",1),We(n.$slots,"head",{},void 0)]),o("p",fo,[o("span",mo,_(e.note),1),e.tip?(r(),i("button",{key:0,type:"button",class:"hint-icon","aria-label":"More info","data-tip":e.tip}," ? ",8,vo)):T("",!0),o("button",{type:"button",class:W(["fold-toggle",{"fold-toggle--folded":u(s)}]),"aria-label":"Collapse or expand "+e.id,"data-tip":"Fold this panel; it stays folded until you open it again",onClick:l[0]||(l[0]=X((...c)=>u(a)&&u(a)(...c),["stop"]))}," ▾ ",10,ko)])]),o("div",yo,[We(n.$slots,"default",{},void 0)])],10,po))}}),Ae=F(go,[["__scopeId","data-v-a03b7f62"]]),wo=["aria-label"],bo=["aria-pressed","onClick"],xo={key:0,class:"segmented__count"},$o=N({__name:"Segmented",props:Ne({options:{},label:{default:""},chips:{type:Boolean}},{modelValue:{required:!0},modelModifiers:{}}),emits:["update:modelValue"],setup(e){const t=vt(e,"modelValue");return(s,a)=>(r(),i("span",{class:W(["segmented",{"segmented--chips":e.chips}]),role:"group","aria-label":e.label},[(r(!0),i(V,null,j(e.options,n=>(r(),i("button",{key:n.id,type:"button",class:"segmented__option","aria-pressed":t.value===n.id,onClick:l=>t.value=n.id},[U(_(n.label),1),n.count!==void 0?(r(),i("b",xo,_(n.count),1)):T("",!0)],8,bo))),128))],10,wo))}}),Te=F($o,[["__scopeId","data-v-7901b5d6"]]),Co={class:"ticket-cell"},So={class:"ticket-cell__summary"},To=N({__name:"TicketCell",props:{row:{}},setup(e){return(t,s)=>(r(),i("td",Co,[O(se,{text:e.row.key,url:e.row.ticket_url,class:"ticket-key"},null,8,["text","url"]),o("span",So,_(e.row.summary),1)]))}}),et=F(To,[["__scopeId","data-v-8436de88"]]),Mo={key:0,class:"pr-cell"},Ao={key:1,class:"pill"},Ro=N({__name:"PrCell",props:{row:{}},setup(e){return(t,s)=>(r(),i("td",null,[e.row.pr?(r(),i("span",Mo,[O(se,{text:e.row.pr,url:e.row.pr_url,class:"pr-cell__link"},null,8,["text","url"]),e.row.pr_state==="OPEN"&&e.row.checks?(r(),i("span",{key:0,class:W(["pill",u(fe)("pill",u(ys)(e.row.checks))])},_(e.row.checks),3)):e.row.pr_state?(r(),i("span",Ao,_(e.row.pr_state.toLowerCase()),1)):T("",!0)])):T("",!0)]))}}),Pt=F(Ro,[["__scopeId","data-v-75c443c3"]]),Eo=["open"],Oo={class:"recently-done__summary"},Do={class:"recently-done__count"},Io={class:"recently-done__list"},Bo={class:"table"},Vo=["data-tip"],Wo={class:"table__actions"},No=N({__name:"RecentlyDone",setup(e){const t=Me("wt-recent-open",!1,{writeDefaults:!1,serializer:{read:n=>n==="1",write:n=>n?"1":"0"}}),s=n=>{t.value=n.target.open},a=w(()=>(ce.value,gs(k.turn).map(n=>({row:n,since:Ze(n.status_since||n.updated)}))));return(n,l)=>a.value.length?(r(),i("details",{key:0,class:"recently-done",open:u(t),onToggle:s},[o("summary",Oo,[l[0]||(l[0]=U(" Recently done ",-1)),o("span",Do,_(a.value.length),1)]),o("div",Io,[o("table",Bo,[l[1]||(l[1]=o("thead",null,[o("tr",null,[o("th",null,"Ticket"),o("th",null,"Status"),o("th",null,"Done"),o("th",{class:"table__actions-col"},[o("span",{class:"visually-hidden"},"Copy for AI")])])],-1)),o("tbody",null,[(r(!0),i(V,null,j(a.value,({row:c,since:h})=>(r(),i("tr",{key:c.row_id},[O(et,{row:c},null,8,["row"]),o("td",null,[o("span",{class:W(["pill",u(fe)("pill",u(Ct)(c.status_group))])},_(c.status),3)]),o("td",null,[o("span",{class:"age","data-tip":"Done "+u(Je)(c.status_since||c.updated)},_(h.text),9,Vo)]),o("td",Wo,[O(de,{class:"table__ai",query:{ws:u(k).workspace.id,kind:"row",ref:c.row_id}},null,8,["query"])])]))),128))])])])],40,Eo)):T("",!0)}}),jo=F(No,[["__scopeId","data-v-12ee20e8"]]),Po=13,Uo=N({__name:"RowScroll",props:{floor:{type:Boolean}},setup(e){const t=I(null),s=I(null),{y:a}=_s(t),{height:n}=at(t),{height:l}=at(s),c=()=>{var y,A,E;return[...((E=(A=(y=s.value)==null?void 0:y.querySelector("table"))==null?void 0:A.tBodies[0])==null?void 0:E.rows)||[]]},h=()=>{const y=t.value;return y?y.getBoundingClientRect().top+y.clientTop+y.clientHeight:0},m=w(()=>(a.value,n.value,l.value,ws(c().map(y=>y.getBoundingClientRect().bottom),h(),Po))),p=()=>{var d,v;const y=t.value,A=c(),E=A[A.length-m.value];if(!y||!E)return;const C=((v=(d=s.value)==null?void 0:d.querySelector("thead"))==null?void 0:v.getBoundingClientRect().bottom)??y.getBoundingClientRect().top;y.scrollBy({top:E.getBoundingClientRect().top-C,behavior:"smooth"})};return(y,A)=>(r(),i(V,null,[o("div",{ref_key:"scroller",ref:t,class:W(["row-scroll",{"row-scroll--floor":e.floor}])},[o("div",{ref_key:"content",ref:s},[We(y.$slots,"default",{},void 0)],512)],2),m.value?(r(),i("button",{key:0,type:"button",class:"row-scroll__more",onClick:p}," +"+_(m.value)+" more ",1)):T("",!0)],64))}}),Ut=F(Uo,[["__scopeId","data-v-7bd9ace7"]]),Fo=I("open"),zo=()=>Fo,qo={class:"table"},Yo={class:"hint-row"},Lo={class:"your-move__next"},Ho=["data-tip"],Xo=["data-tip"],Ko=["data-tip"],Go={key:0,class:"alert alert--inline your-move__problem",role:"alert"},Jo={class:"table__actions"},Zo=["aria-label","onClick"],Qo={key:0,class:"empty"},ea=N({__name:"YourMove",setup(e){const t=zo(),s=w(()=>{ce.value;const h=bs(k.turn,t.value);return{...h,rows:h.rows.map(m=>({...m,age:Ze(m.row.activity_at)}))}}),a=w(()=>[{id:"open",label:"Open",count:s.value.open},{id:"handled",label:"Handled",count:s.value.handled}]),n=w(()=>{if(t.value==="handled")return"Nothing marked as handled.";const{noData:h,failing:m}=Nt(k.health,k.polled_at,k.workspace.connectors.length,g.live);return h?"No data: "+m.join(", ")+(m.length===1?" is":" are")+" failing. Check Settings.":"Nothing is waiting on you."}),l=Ye(new Map);Qe(()=>l.clear());const c=async h=>{const m=h.row.row_id,p=await ze({handled:{[m]:h.handled?null:new Date().toISOString()}});p?l.set(m,p):l.delete(m)};return(h,m)=>(r(),q(Ae,{id:"your-move",title:"Your move",note:"Next step is yours",tip:"Each row is something where the next step is yours. Ticket, PR and comment open in a new tab."},{head:te(()=>[O(Te,{modelValue:u(t),"onUpdate:modelValue":m[0]||(m[0]=p=>Zt(t)?t.value=p:null),options:a.value,label:"Show",class:"your-move__view"},null,8,["modelValue","options"])]),default:te(()=>[O(Ut,{floor:""},{default:te(()=>[o("table",qo,[m[1]||(m[1]=o("thead",null,[o("tr",null,[o("th",null,"Ticket"),o("th",null,"Next step"),o("th",null,"PR"),o("th",{class:"hide-narrow"},"Last comment"),o("th",{class:"table__actions-col"},[o("span",{class:"visually-hidden"},"Mark")])])],-1)),o("tbody",null,[(r(!0),i(V,null,j(s.value.rows,p=>(r(),i("tr",{key:p.row.row_id,class:W(["your-move__row",{"your-move__row--handled":p.handled}])},[O(et,{row:p.row},null,8,["row"]),o("td",null,[o("span",Yo,[o("span",Lo,_(p.row.next),1),p.age.text?(r(),i("span",{key:0,class:W(["age",{"age--old":p.age.old}]),"data-tip":"Waiting since "+u(Je)(p.row.activity_at)},_(p.age.text),11,Ho)):T("",!0),p.row.why?(r(),i("button",{key:1,type:"button",class:"hint-icon","aria-label":"Why this is your move","data-tip":p.row.why}," ? ",8,Xo)):T("",!0),p.handled?(r(),i("span",{key:2,class:"handled-chip","data-tip":"Marked "+u(ne)(u(k).marks.handled[p.row.row_id]||"")+". Comes back in 7 days if it is still open, or sooner when something new happens."},"handled",8,Ko)):T("",!0)]),l.get(p.row.row_id)?(r(),i("p",Go,_(l.get(p.row.row_id)),1)):T("",!0)]),O(Pt,{row:p.row},null,8,["row"]),o("td",{class:W(["hide-narrow",{"your-move__quote":p.row.comment}])},[p.row.comment?(r(),q(se,{key:0,text:p.row.comment,url:p.row.comment_url},null,8,["text","url"])):T("",!0)],2),o("td",Jo,[O(de,{class:"table__ai",query:{ws:u(k).workspace.id,kind:"row",ref:p.row.row_id}},null,8,["query"]),o("button",{type:"button",class:W(["your-move__mark",{"your-move__mark--undo":p.handled}]),"aria-label":(p.handled?"Move back to open: ":"Mark as handled: ")+p.row.key,onClick:X(y=>c(p),["stop"])},_(p.handled?"Undo":"Handled"),11,Zo)])],2))),128))])])]),_:1}),s.value.rows.length?T("",!0):(r(),i("p",Qo,_(n.value),1)),u(t)==="open"?(r(),q(jo,{key:1})):T("",!0)]),_:1}))}}),ta=F(ea,[["__scopeId","data-v-e3e1502a"]]),sa=I(!1),na=()=>sa,oa={class:"todos__add"},aa={key:0,class:"alert alert--inline",role:"alert"},la={class:"todos__list"},ra=["aria-checked","aria-label","onClick"],ia={class:"todos__text"},ca={class:"todos__meta"},da=["onClick"],ua={key:0,class:"empty todos__empty"},pa=N({__name:"Todos",setup(e){const t=na(),s=I(""),a=w(()=>(k.todos||[]).filter(d=>!d.done)),n=w(()=>(k.todos||[]).filter(d=>d.done)),l=w(()=>new RegExp(k.workspace.key_pattern||"(?!)","g")),c=d=>{const v=[];let f=0;const B=d.text;for(const z of B.matchAll(l.value))z.index>f&&v.push({text:B.slice(f,z.index)}),v.push({text:z[0],url:z[0]===d.key?d.ticket_url:""}),f=z.index+z[0].length;return f<B.length&&v.push({text:B.slice(f)}),v},h=w(()=>(ce.value,(t.value?a.value.concat(n.value):a.value).map(d=>({todo:d,parts:c(d),meta:(d.by&&d.by!=="you"?d.by+" · ":"")+ne(d.created)})))),m=I("");Qe(()=>{m.value=""});const p=async d=>(m.value=await on(d),!m.value),y=async()=>{const d=s.value.trim();d&&await p({action:"add",text:d})&&s.value.trim()===d&&(s.value="")},A=d=>p({action:d.done?"undo":"done",id:d.id}),E=d=>p({action:"delete",id:d.id}),R=w({get:()=>t.value?"done":"open",set:d=>{t.value=d==="done"}}),C=w(()=>[{id:"open",label:"Open",count:a.value.length},{id:"done",label:"Done",count:n.value.length}]);return(d,v)=>(r(),q(Ae,{id:"todo",title:"To do",note:"Your own notes",tip:'Your personal to-dos, kept on this machine. Add here, or ask Claude: "add to my to-do: …". A ticket key like PROJ-123 in the text becomes a link.'},{head:te(()=>[O(Te,{modelValue:R.value,"onUpdate:modelValue":v[0]||(v[0]=f=>R.value=f),options:C.value,label:"Show",class:"todos__view"},null,8,["modelValue","options"])]),default:te(()=>[o("div",oa,[Le(o("input",{"onUpdate:modelValue":v[1]||(v[1]=f=>s.value=f),class:"input todos__input",type:"text",placeholder:"Add a to-do and press Enter","aria-label":"New to-do",autocomplete:"off",onKeydown:je(y,["enter"])},null,544),[[He,s.value]]),o("button",{type:"button",class:"btn btn--tonal",onClick:y},"Add")]),m.value?(r(),i("p",aa,_(m.value),1)):T("",!0),o("ul",la,[(r(!0),i(V,null,j(h.value,({todo:f,parts:B,meta:z})=>(r(),i("li",{key:f.id,class:W(["todos__item",{"todos__item--done":f.done}])},[o("button",{type:"button",class:W(["todos__check",{"todos__check--on":f.done}]),role:"checkbox","aria-checked":!!f.done,"aria-label":f.done?"Mark not done":"Mark done",onClick:L=>A(f)},null,10,ra),o("span",ia,[(r(!0),i(V,null,j(B,(L,J)=>(r(),i(V,{key:J},[L.url!==void 0?(r(),q(se,{key:0,text:L.text,url:L.url},null,8,["text","url"])):(r(),i(V,{key:1},[U(_(L.text),1)],64))],64))),128))]),o("span",ca,_(z),1),o("button",{type:"button",class:"todos__delete","aria-label":"Delete to-do",onClick:L=>E(f)}," × ",8,da)],2))),128)),h.value.length?T("",!0):(r(),i("span",ua,_(u(t)?"Nothing here yet.":"Nothing to do. Add one above, or ask Claude."),1))])]),_:1}))}}),_a=F(pa,[["__scopeId","data-v-6f034e1e"]]),ha={key:0,class:"empty"},fa={class:"table waiting__table"},ma=["data-tip"],va={class:"hide-narrow"},ka={key:0,class:"hint-row"},ya=["data-tip"],ga={class:"table__actions"},wa=N({__name:"Waiting",setup(e){const t=w(()=>(ce.value,xs(k.turn).map(s=>({row:s,since:s.status_since?Ze(s.status_since):null}))));return(s,a)=>(r(),q(Ae,{id:"waiting",title:"Waiting on others",note:"Someone else holds the next step",tip:"Tickets you touched in the last 30 days where someone else holds the next step: a reviewer, QA, or a new owner."},{default:te(()=>[t.value.length?(r(),q(Ut,{key:1},{default:te(()=>[o("table",fa,[a[0]||(a[0]=o("thead",null,[o("tr",null,[o("th",{class:"waiting__col waiting__col--ticket"},"Ticket"),o("th",{class:"waiting__col waiting__col--status"},"Status"),o("th",{class:"waiting__col waiting__col--with"},"With"),o("th",{class:"hide-narrow"},"Waiting for"),o("th",{class:"waiting__col waiting__col--pr"},"PR"),o("th",{class:"table__actions-col waiting__col waiting__col--actions"},[o("span",{class:"visually-hidden"},"Copy for AI")])])],-1)),o("tbody",null,[(r(!0),i(V,null,j(t.value,({row:n,since:l})=>(r(),i("tr",{key:n.row_id},[O(et,{row:n},null,8,["row"]),o("td",null,[o("span",{class:W(["pill",u(fe)("pill",u(Ct)(n.status_group))])},_(n.status),3)]),o("td",null,[U(_(n.whose)+" ",1),l?(r(),i("span",{key:0,class:W(["age",{"age--old":l.hours>=72}]),"data-tip":n.status+" since "+u(Je)(n.status_since)},_(l.text),11,ma)):T("",!0)]),o("td",va,[n.hold_url?(r(),i("span",ka,[O(se,{text:n.next,url:n.hold_url,class:"waiting__reason"},null,8,["text","url"]),o("button",{type:"button",class:"hint-icon","aria-label":"Full reason","data-tip":n.hold_full||n.next}," ? ",8,ya)])):(r(),i(V,{key:1},[U(_(n.next),1)],64))]),O(Pt,{row:n},null,8,["row"]),o("td",ga,[O(de,{class:"table__ai",query:{ws:u(k).workspace.id,kind:"row",ref:n.row_id}},null,8,["query"])])]))),128))])])]),_:1})):(r(),i("p",ha,"Nothing is waiting on someone else."))]),_:1}))}}),ba=F(wa,[["__scopeId","data-v-a632447c"]]),xa=(e,t)=>e.seen instanceof Set?e.seen.has(t):e.seen.includes(t),Ft=(e,t)=>new Date(e.at).getTime()>(t.base||0)&&!xa(t,e.id),$a=(e,t)=>{const s=String(t||"").toLowerCase().split(/\s+/).filter(Boolean);if(!s.length)return!0;const a=[e.ref,e.key,e.title,e.kind,e.who,e.text,e.body].join(" ").toLowerCase();return s.every(n=>a.includes(n))},Ca=e=>e.key||e.ref||e.id,Sa=(e,t)=>{const s=new Map;for(const a of e){const n=Ca(a),l=s.get(n)||{id:n,items:[],newest:a.at,unread:0,title:""};s.set(n,l),l.items.push(a),l.title||(l.title=a.title||""),a.at>l.newest&&(l.newest=a.at),Ft(a,t)&&(l.unread+=1)}return[...s.values()].sort((a,n)=>String(n.newest).localeCompare(String(a.newest)))},Ta=e=>new Set(e.filter(t=>t.group==="you"&&!t.handled).map(t=>t.key)),Ma=(e,t=2)=>{const s=[...new Set(e.map(n=>n.kind))],a=s.length-t;return s.slice(0,t).join(" · ")+(a>0?` +${a}`:"")},Aa=e=>/fail|changes/.test(e)?"bad":/approved|merged|passed/.test(e)?"ok":"",Ra=e=>/comment|approved|changes|mentioned|review requested/.test(e.kind)&&String(e.body||"").trim()!=="",zt=e=>e.ref!==e.key&&(e.key||e.title)||"",Ea=e=>[zt(e),e.text===e.title?"":e.text].filter(Boolean).join(" · "),Oa=e=>e.type==="github"&&e.label==="GitHub"?"GH":Wt(e.label),Da={class:"feed-item__body"},Ia={class:"feed-item__top"},Ba={key:0,class:"feed-item__who"},Va={key:1,class:"at-you"},Wa={class:"feed-item__right"},Na={class:"timestamp feed-item__when"},ja=["aria-label","aria-expanded"],Pa={key:0,class:"feed-item__text"},Ua={class:"feed-item__full"},Fa={class:"feed-item__actions"},za={key:1,class:"feed-item__text"},qa=N({__name:"FeedItem",props:{item:{},source:{},fresh:{type:Boolean},lit:{type:Boolean},expanded:{type:Boolean}},emits:["open","toggle","seen"],setup(e,{emit:t}){const s=e,a=t,n=w(()=>Oa(s.source)),l=w(()=>zt(s.item)),c=w(()=>Ea(s.item)),h=w(()=>Aa(s.item.kind)),m=w(()=>h.value||(s.source.type==="github"?"":"jira")),p=w(()=>Ra(s.item)),y=w(()=>p.value&&s.expanded),A=w(()=>(ce.value,ne(s.item.at))),E=()=>{s.fresh&&a("seen",s.item.id)},R=()=>{a("toggle",s.item.id),E()};return(C,d)=>(r(),i("li",{class:W(["feed-item",{"feed-item--new":e.fresh,"feed-item--lit":e.lit,"feed-item--open":y.value}]),onClick:E},[o("span",{class:W(["feed-item__icon",u(fe)("feed-item__icon",m.value)])},_(n.value),3),o("div",Da,[o("div",Ia,[O(se,{text:e.item.ref,url:e.item.url},null,8,["text","url"]),o("span",{class:W(["feed-item__kind",{"feed-item__kind--bad":h.value==="bad"}])},_(e.item.kind),3),e.item.who?(r(),i("span",Ba,_(e.item.who),1)):T("",!0),e.item.mention?(r(),i("span",Va,"@you")):T("",!0),o("span",Wa,[o("span",Na,_(A.value),1),p.value?(r(),i("button",{key:0,type:"button",class:"feed-item__toggle","aria-label":y.value?"Collapse":"Expand","aria-expanded":y.value,onClick:X(R,["stop"])},null,8,ja)):T("",!0)])]),y.value?(r(),i(V,{key:0},[l.value?(r(),i("span",Pa,_(l.value),1)):T("",!0),o("div",Ua,_(e.item.body),1),o("div",Fa,[o("button",{type:"button",class:"btn btn--tonal",onClick:d[0]||(d[0]=X(v=>a("open",e.item),["stop"]))}," Open formatted "),O(de,{query:{ws:u(k).workspace.id,kind:"feed",ref:e.item.id}},null,8,["query"]),O(se,{text:"Open in "+e.source.label,url:e.item.url,class:"btn"},null,8,["text","url"])])],64)):c.value?(r(),i("span",za,_(c.value),1)):T("",!0)])],2))}}),ft=F(qa,[["__scopeId","data-v-27e556e1"]]),Ya=Ye({source:"all",view:Me("wt-feed-view","ticket",{writeDefaults:!1}),query:"",openGroups:new Set}),La=()=>Ya,Ha={key:0,class:"feed__new-count"},Xa={class:"feed__bar"},Ka={key:0,class:"alert alert--inline",role:"alert"},Ga={class:"feed__search"},Ja={class:"feed__search-count"},Za={key:1,class:"empty"},Qa={key:2,class:"feed__list"},el=["title","onClick"],tl=["aria-expanded","aria-label"],sl={class:"feed__group-text"},nl={class:"feed__group-key"},ol={key:0,class:"feed__group-sum"},al={class:"feed__group-meta"},ll={class:"feed__group-kinds"},rl={class:"timestamp feed__group-when"},il={key:0,class:"feed__children"},cl=N({__name:"Feed",emits:["open"],setup(e,{emit:t}){const s=t,a=La(),n=[{id:"ticket",label:"By ticket"},{id:"flat",label:"Flat"}],l=w(()=>k.workspace.connectors||[]),c=w(()=>Object.fromEntries(l.value.map(x=>[x.id,x]))),h=x=>c.value[x]||{id:x,type:x,label:x},m=w(()=>l.value.length>1?[{id:"all",label:"All"}].concat(l.value.map(x=>({id:x.id,label:x.label}))):[]),p=w({get:()=>m.value.some(x=>x.id===a.source)?a.source:"all",set:x=>{a.source=x}}),y=w(()=>({base:k.marks.base,seen:new Set(k.marks.seen)})),A=x=>Ft(x,y.value),E=w(()=>k.feed.filter(A).length),R=w(()=>k.feed.filter(x=>p.value==="all"||x.source===p.value)),C=w(()=>R.value.filter(x=>$a(x,a.query)).slice(0,200)),d=w(()=>new Map(k.turn.map(x=>[x.key,x]))),v=w(()=>Ta(k.turn)),f=w(()=>(ce.value,Sa(C.value,y.value).map(x=>({...x,row:d.value.get(x.id),move:v.value.has(x.id),when:ne(x.newest),kinds:Ma(x.items),open:a.openGroups.has(x.id)||!!a.query})))),B=(x,Y)=>{x.has(Y)?x.delete(Y):x.add(Y)},z=x=>B(a.openGroups,x.id),L=x=>B(g.open,x),J=x=>ze({seen:[x]}).then(ae),ue=I("");Qe(()=>{ue.value=""});const Q=async()=>{ue.value=await ze({seen:k.feed.map(x=>x.id),base:Date.now()})};return(x,Y)=>(r(),q(Ae,{id:"feed",title:"What changed",note:"Newest first, every source",tip:"Newest first: comments, reviews, merges and failed checks on your PRs, and ticket changes. Each item opens where it happened.",class:"feed",column:""},{head:te(()=>[E.value?(r(),i("span",Ha,_(E.value)+" new",1)):T("",!0)]),default:te(()=>[o("div",Xa,[m.value.length?(r(),q(Te,{key:0,modelValue:p.value,"onUpdate:modelValue":Y[0]||(Y[0]=M=>p.value=M),options:m.value,label:"Feed source"},null,8,["modelValue","options"])):T("",!0),O(Te,{modelValue:u(a).view,"onUpdate:modelValue":Y[1]||(Y[1]=M=>u(a).view=M),options:n,label:"Feed view"},null,8,["modelValue"])]),ue.value?(r(),i("p",Ka,_(ue.value),1)):T("",!0),o("div",Ga,[Le(o("input",{"onUpdate:modelValue":Y[2]||(Y[2]=M=>u(a).query=M),class:"input feed__search-input",type:"search",placeholder:"Search the feed: key, author, words…","aria-label":"Search the feed",autocomplete:"off"},null,512),[[He,u(a).query,void 0,{trim:!0}]]),o("span",Ja,_(u(a).query?C.value.length+" of "+R.value.length:""),1),o("button",{type:"button",class:"feed__mark-read",onClick:Q},"Mark all read")]),u(k).feed.length?(r(),i("ol",Qa,[u(a).view==="ticket"?(r(!0),i(V,{key:0},j(f.value,M=>(r(),i("li",{key:M.id,class:W(["feed__group",{"feed__group--new":M.unread,"feed__group--open":M.open,"feed__group--move":M.move}])},[o("div",{class:"feed__group-head",title:M.move?"Your move on this ticket":void 0,onClick:Z=>z(M)},[o("button",{type:"button",class:"feed__group-caret","aria-expanded":M.open,"aria-label":"Collapse or expand "+M.id}," › ",8,tl),o("div",sl,[o("div",nl,[O(se,{text:M.id,url:M.row?M.row.ticket_url:M.items[0].url,class:"ticket-key feed__group-ticket"},null,8,["text","url"]),M.row||M.title?(r(),i("span",ol,_(M.row?M.row.summary:M.title),1)):T("",!0)]),o("div",al,[o("span",{class:W(["feed__group-count",{"feed__group-count--new":M.unread}])},_(M.unread?M.unread+" new":M.items.length),3),o("span",ll,_(M.kinds),1),o("span",rl,_(M.when),1)])]),O(de,{class:"feed__group-copy",query:{ws:u(k).workspace.id,kind:"thread",ref:M.id}},null,8,["query"])],8,el),M.open?(r(),i("ul",il,[(r(!0),i(V,null,j(M.items,Z=>(r(),q(ft,{key:Z.id,item:Z,source:h(Z.source),fresh:A(Z),lit:!!M.unread,expanded:u(g).open.has(Z.id),onOpen:Y[3]||(Y[3]=Re=>s("open",Re)),onToggle:L,onSeen:J},null,8,["item","source","fresh","lit","expanded"]))),128))])):T("",!0)],2))),128)):(r(!0),i(V,{key:1},j(C.value,M=>(r(),q(ft,{key:M.id,item:M,source:h(M.source),fresh:A(M),expanded:u(g).open.has(M.id),onOpen:Y[4]||(Y[4]=Z=>s("open",Z)),onToggle:L,onSeen:J},null,8,["item","source","fresh","expanded"]))),128))])):(r(),i("p",Za," No changes yet. New comments, reviews and status changes show up here. "))]),_:1}))}}),dl=F(cl,[["__scopeId","data-v-d92a7c74"]]),ul={id:"first-run",class:"island first-run","aria-labelledby":"first-run-title"},pl={class:"first-run__text"},_l={class:"first-run__actions"},hl=["aria-disabled"],fl=N({__name:"FirstRun",setup(e){const t=I(!1),s=()=>Js("connectors"),a=()=>{t.value||(t.value=!0,ln().finally(()=>{t.value=!1}))};return(n,l)=>(r(),i("section",ul,[l[1]||(l[1]=o("h2",{id:"first-run-title",class:"first-run__title"}," Whose Turn watches your tickets and pull requests and shows what is waiting on you. ",-1)),o("p",pl,[o("b",null,_(u(k).workspace.name),1),l[0]||(l[0]=U(" has no sources yet, so there is nothing to watch. Connect a repository or a tracker, or look around a demo workspace with invented data first. ",-1))]),o("div",_l,[o("button",{type:"button",class:"btn btn--tonal",onClick:s},"Add a source"),o("button",{type:"button",class:"btn","aria-disabled":t.value,onClick:a}," Try a demo workspace ",8,hl)]),l[2]||(l[2]=o("p",{class:"first-run__text first-run__ai"},' Or ask Claude: "add my GitHub repository to Whose Turn." ',-1))]))}}),ml=F(fl,[["__scopeId","data-v-ef746675"]]),vl=new RegExp("(`[^`]{1,500}`)|(\\*\\*[^*]{1,500}\\*\\*)|(\\[[^\\]]{1,300}\\]\\(https?:\\/\\/[^)\\s]{1,2000}\\))|(https?:\\/\\/[^\\s<>()]{1,2000})|(@[A-Za-z0-9-]+(?: [A-Z][a-z]+)?)|((?<![A-Za-z0-9])\\*[^*\\s][^*]{0,500}\\*(?![A-Za-z0-9])|(?<![A-Za-z0-9])_[^_\\s][^_]{0,500}_(?![A-Za-z0-9]))","g"),mt=(e,t)=>{const s=Ge(t);return s?{type:"link",text:e,href:s}:{type:"text",text:e}},kl=e=>{const t=e[0];if(e[1])return{type:"code",text:t.slice(1,-1)};if(e[2])return{type:"strong",text:t.slice(2,-2)};if(e[3]){const s=t.indexOf("](");return mt(t.slice(1,s),t.slice(s+2,-1))}return e[4]?mt(t,t):e[5]?{type:"mention",text:t}:{type:"em",text:t.slice(1,-1)}},ge=e=>{const t=new RegExp(vl),s=[];let a=0,n;for(;n=t.exec(e);)n.index>a&&s.push({type:"text",text:e.slice(a,n.index)}),s.push(kl(n)),a=n.index+n[0].length;return a<e.length&&s.push({type:"text",text:e.slice(a)}),s},yl=e=>{const t=String(e||"").replace(/<!--[\s\S]*?-->/g,"").split(`
|
|
2
|
-
`),s=[];let a=[],n=null;const l=()=>{a.length&&(s.push({type:"paragraph",lines:a.map(ge)}),a=[]),n=null};for(let c=0;c<t.length;c++){const h=t[c];if(/^\s*```/.test(h)){l();const A=[];for(c++;c<t.length&&!/^\s*```/.test(t[c]);c++)A.push(t[c]);s.push({type:"fence",text:A.join(`
|
|
3
|
-
`)});continue}const m=h.match(/^(#{1,6})\s+(.*)$/);if(m){l(),s.push({type:"heading",level:m[1].length<=2?4:5,inline:ge(m[2])});continue}const p=h.match(/^>\s?(.*)$/);if(p){l(),s.push({type:"quote",inline:ge(p[1])});continue}const y=h.match(/^\s*([-*+]|\d+[.)])\s+(.*)$/);if(y){const A=n;a.length&&(l(),n=A);const E=/\d/.test(y[1]),R=n,C=R&&R.ordered===E?R:{type:"list",ordered:E,items:[]};C!==R&&s.push(C),n=C,C.items.push(ge(y[2]));continue}if(!h.trim()){l();continue}n=null,a.push(h)}return l(),s},be=(e,t,s="")=>{const a=document.createElement(e);return a.textContent=t,s&&(a.className=s),a},gl=e=>{if(e.type==="text")return document.createTextNode(e.text);if(e.type==="link"){const t=be("a",e.text);return t.setAttribute("href",e.href),t.setAttribute("target","_blank"),t.setAttribute("rel","noopener"),t}return e.type==="mention"?be("span",e.text,"markdown-view__mention"):be(e.type,e.text)},qt=(e,t)=>{e.forEach(s=>t.append(gl(s)))},Ve=(e,t)=>{const s=document.createElement(e);return qt(t,s),s},wl=e=>{if(e.type==="fence"){const s=document.createElement("pre");return s.append(be("code",e.text)),s}if(e.type==="heading")return Ve(`h${e.level}`,e.inline);if(e.type==="quote")return Ve("blockquote",e.inline);if(e.type==="list"){const s=document.createElement(e.ordered?"ol":"ul");return e.items.forEach(a=>s.append(Ve("li",a))),s}const t=document.createElement("p");return e.lines.forEach((s,a)=>{a&&t.append(document.createElement("br")),qt(s,t)}),t},bl=e=>{const t=document.createDocumentFragment();return yl(e).forEach(s=>t.append(wl(s))),t},xl=N({__name:"MarkdownView",props:{source:{default:""}},setup(e){const t=e,s=I(null);return Qt(()=>{s.value&&s.value.replaceChildren(bl(t.source))}),(a,n)=>(r(),i("div",{ref_key:"host",ref:s,class:"markdown-view"},null,512))}}),$l=F(xl,[["__scopeId","data-v-dcf42e95"]]),Cl={class:"modal__card"},Sl={class:"modal__head"},Tl={id:"modal-title",class:"modal__title"},Ml=["href"],Al=N({__name:"CommentModal",setup(e){const t=w(()=>g.modal),s=w(()=>t.value?[t.value.ref,t.value.kind,t.value.who].filter(Boolean).join(" · "):""),a=w(()=>t.value?Ge(t.value.url):""),n=w(()=>{var h;const c=(k.workspace.connectors||[]).find(m=>{var p;return m.id===((p=t.value)==null?void 0:p.source)});return"Open in "+(c?c.label:(h=t.value)==null?void 0:h.source)}),l=()=>{g.modal=null};return(c,h)=>t.value?(r(),i("div",{key:0,class:"modal",role:"dialog","aria-modal":"true","aria-labelledby":"modal-title",onClick:X(l,["self"])},[o("div",Cl,[o("div",Sl,[o("h3",Tl,_(s.value),1),a.value?(r(),i("a",{key:0,class:"btn",href:a.value,target:"_blank",rel:"noopener"},_(n.value),9,Ml)):T("",!0),o("button",{type:"button",class:"btn btn--tonal",onClick:l},"Close")]),O($l,{source:t.value.body||t.value.text||""},null,8,["source"])])])):T("",!0)}}),Rl={class:"modal__card feedback-sheet__card"},El={class:"modal__head"},Ol={class:"modal__meta"},Dl=["aria-disabled"],Il={key:0,class:"alert"},Bl={class:"sheet-form"},Vl=["placeholder","onKeydown"],Wl={key:0,class:"feedback-sheet__note"},Nl={key:1,class:"feedback-sheet__note"},jl={key:2,class:"empty"},Pl=["href"],Ul={key:3,class:"feedback-sheet__reports"},Fl=["href"],zl={class:"feedback-sheet__report-title"},ql={class:"timestamp"},Yl=N({__name:"FeedbackSheet",setup(e){const t=I(""),s=I(null),a=I(!1),n=I([]),l=I(null),c=I(""),h=I(!0),m=Ke("",1600),p=I([]),y=d=>"/report?id="+encodeURIComponent(d),A=()=>he("/api/feedback").then(d=>{h.value=d.configured,c.value=d.configured?"Linear · "+d.project:"not connected",p.value=d.reports||[]},()=>{n.value=[xe+"; past reports cannot be loaded."]}),E=()=>{g.feedback=!1},R=()=>{const d=t.value.trim();if(!d){n.value=["Write at least one line."];return}n.value=[],navigator.clipboard.writeText(d).then(()=>{m.value="sent"},()=>{m.value="failed"})},C=()=>{if(!h.value||a.value)return;const d=t.value.trim();if(!d){n.value=["Write at least one line."];return}a.value=!0,le("/api/feedback",{text:d,from:"the window",ws:k.workspace.id,kind_ref:{kind:"workspace",ref:k.workspace.id}}).then(v=>{if(a.value=!1,v.error){n.value=St(v.error);return}l.value=v.issue||null,t.value="",p.value=v.reports||p.value}).catch(v=>{a.value=!1,n.value=[oe(v,{refused:"Not sent",unreachable:Pe})]})};return K(()=>g.feedback,d=>{d&&(n.value=[],l.value=null,A().then(()=>kt(()=>{var v;return(v=s.value)==null?void 0:v.focus()})))}),(d,v)=>u(g).feedback?(r(),i("div",{key:0,class:"modal",role:"dialog","aria-modal":"true","aria-labelledby":"feedback-title",onClick:X(E,["self"])},[o("div",Rl,[o("div",El,[v[1]||(v[1]=o("h3",{id:"feedback-title",class:"modal__title"},"To the Whose Turn backlog",-1)),o("span",Ol,_(c.value),1),o("button",{type:"button",class:"btn btn--tonal","aria-disabled":!h.value||a.value,onClick:C},_(a.value?"Composing…":"Send"),9,Dl),o("button",{type:"button",class:"btn",onClick:E},"Close")]),n.value.length?(r(),i("div",Il,_(n.value.join(" · ")),1)):T("",!0),o("div",Bl,[Le(o("textarea",{ref_key:"field",ref:s,"onUpdate:modelValue":v[0]||(v[0]=f=>t.value=f),class:"input feedback-sheet__text",rows:"5",placeholder:"Describe the problem or the wish in your own words."+(h.value?" Cmd+Enter sends.":""),autocomplete:"off",onKeydown:[je(X(C,["meta","prevent"]),["enter"]),je(X(C,["ctrl","prevent"]),["enter"])]},null,40,Vl),[[He,t.value]]),h.value?(r(),i("p",Nl," An assistant turns it into a proper issue (title, kind, description) and files it in the Whose Turn backlog with this workspace's summary attached. ")):(r(),i("p",Wl,[v[2]||(v[2]=U(" The maintainer's tracker is not connected on this machine, so nothing can be sent from here. Copy your remark and pass it on yourself. ",-1)),o("button",{type:"button",class:"btn",onClick:R},_(u(m)==="sent"?"Copied":u(m)==="failed"?"Could not copy":"Copy remark"),1)])),l.value?(r(),i("p",jl,[v[3]||(v[3]=U(" Filed as ",-1)),o("a",{href:y(l.value.identifier),target:"_blank",rel:"noopener"},_(l.value.identifier),9,Pl),U(" · "+_(l.value.title),1)])):T("",!0),p.value.length?(r(),i("div",Ul,[v[4]||(v[4]=o("h4",{class:"sheet-form__heading"},"Your reports",-1)),(r(!0),i(V,null,j(p.value,f=>(r(),i("div",{key:f.id,class:"feedback-sheet__report"},[o("a",{href:y(f.id),target:"_blank",rel:"noopener"},_(f.id),9,Fl),o("span",zl,_(f.title),1),o("span",{class:W(["pill",u(fe)("pill",u($s)(f.outcome))])},_(f.state||"filed"),3),o("span",ql,_(u(ne)(f.changed_at||f.filed_at)),1)]))),128))])):T("",!0)])])])):T("",!0)}}),Ll=F(Yl,[["__scopeId","data-v-ec34adcc"]]),Hl={class:"field-row"},Xl={class:"field-row__label"},Kl={key:0,class:"field-row__check"},Gl=["checked"],Jl={key:2,class:"field-row__inline"},Zl=["value"],Ql=["value"],er=["value"],tr=["placeholder","value"],sr={key:5,class:"field-row__hint"},nr=N({__name:"FieldRow",props:Ne({spec:{},placeholder:{default:""},hint:{default:""}},{modelValue:{type:[String,Array,Boolean]},modelModifiers:{}}),emits:Ne(["secret"],["update:modelValue"]),setup(e,{emit:t}){const s=e,a=t,n=vt(e,"modelValue"),l=/^#[0-9a-f]{6}$/i,c=()=>String(n.value??""),h=()=>n.value||[],m=R=>{n.value=R},p=()=>n.value??s.spec.default??!1,y=R=>{n.value=R===(s.spec.default??!1)?void 0:R},A=R=>R.target.value,E=R=>m(R.split(",").map(C=>C.trim()).filter(Boolean));return(R,C)=>(r(),i("div",Hl,[o("label",Xl,_(e.spec.label)+_(e.spec.required?" *":""),1),e.spec.boolean?(r(),i("label",Kl,[o("input",{type:"checkbox",checked:p(),onChange:C[0]||(C[0]=d=>y(d.target.checked))},null,40,Gl),U(" "+_(e.hint||e.spec.hint),1)])):e.spec.secret?(r(),i("input",{key:1,class:"input",type:"password",placeholder:"paste to replace",autocomplete:"off",onInput:C[1]||(C[1]=d=>a("secret",e.spec.key,A(d)))},null,32)):e.spec.colour?(r(),i("div",Jl,[o("input",{class:"input field-row__colour",type:"color",value:l.test(c())?c():"#888888",onInput:C[2]||(C[2]=d=>m(A(d)))},null,40,Zl),o("input",{class:"input",type:"text",placeholder:"#rrggbb",value:c(),onInput:C[3]||(C[3]=d=>m(A(d)))},null,40,Ql),o("button",{type:"button",class:"btn",onClick:C[4]||(C[4]=d=>m(""))},"None")])):e.spec.list?(r(),i("input",{key:3,class:"input",type:"text",value:h().join(", "),onInput:C[5]||(C[5]=d=>E(A(d)))},null,40,er)):(r(),i("input",{key:4,class:"input",type:"text",placeholder:e.placeholder,value:c(),onInput:C[6]||(C[6]=d=>m(A(d)))},null,40,tr)),!e.spec.boolean&&(e.hint||e.spec.hint)?(r(),i("small",sr,_(e.hint||e.spec.hint),1)):T("",!0)]))}}),ie=F(nr,[["__scopeId","data-v-2ba0ba86"]]),or={class:"modal__card settings-sheet__card"},ar={class:"modal__head"},lr={class:"modal__meta"},rr={key:0,class:"settings-sheet__dirty"},ir={key:0,class:"settings-sheet__confirm",role:"alertdialog"},cr={class:"settings-sheet__confirm-text"},dr={key:1,class:"alert settings-sheet__alert",role:"alert"},ur={key:2,class:"alert settings-sheet__alert"},pr={key:3,class:"settings-sheet__body"},_r={class:"settings-sheet__list"},hr=["aria-current","onClick"],fr={class:"sheet-form"},mr={key:0,class:"empty"},vr={ref:"connectors-head",class:"sheet-form__heading"},kr={class:"settings-sheet__connector-head"},yr={class:"settings-sheet__connector-name"},gr=["onClick"],wr={class:"settings-sheet__actions"},br=["onClick"],xr={key:0,class:"settings-sheet__rules"},$r={class:"settings-sheet__rule-when"},Cr={class:"settings-sheet__rule-then"},Sr={key:1,class:"empty"},Tr=N({__name:"SettingsSheet",setup(e){const t=I([]),s=I(0),a=I([]),n=I({workspace:[],theme:[],rules:[],statuses:[]}),l=I(""),c=I([]),h=I({}),m=es("connectors-head"),p=new Map,y=I(0),A=I(""),E=I(null),R=I(""),C=w(()=>g.settings&&!R.value&&(y.value>0||dt(t.value)!==A.value)),d={key:"id",label:"Entry id"},v={key:"label",label:"Label"},f=w(()=>t.value[s.value]),B=w(()=>(f.value&&h.value[f.value.id]||[]).map(Ys)),z=b=>a.value.find(S=>S.type===b.type)||{type:b.type,label:b.type,fields:[]},L=w(()=>t.value.map((b,S)=>({uid:ct(b),label:b.name||b.id||"Workspace "+(S+1)}))),J=w(()=>{var b;return(((b=f.value)==null?void 0:b.connectors)||[]).map(S=>({entry:S,uid:ct(S),spec:z(S)}))}),ue=b=>{var $;const S=($=k.workspaces.find(P=>P.id===b))==null?void 0:$.theme_seed;return S?`empty: ${S}, derived from the workspace id`:"empty derives one from the workspace id"},Q=(b,S)=>b[S],x=(b,S,$)=>{p.set(b,Object.assign(p.get(b)||{},{[S]:$})),y.value+=1},Y=()=>{t.value.push(rt({id:"new-"+(t.value.length+1),name:"New workspace"})),s.value=t.value.length-1},M=()=>{t.value.splice(s.value,1),s.value=Math.max(0,s.value-1)},Z=()=>{f.value&&(E.value={text:`Delete ${f.value.name||f.value.id}? Its to-dos and marks stay on disk.`,yes:"Delete",no:"Keep",run:M})},Re=b=>{var S;return(S=f.value)==null?void 0:S.connectors.push({type:b.type})},Yt=b=>{f.value&&(p.delete(f.value.connectors[b]),f.value.connectors.splice(b,1))},Lt=b=>{if(!f.value)return;const S=z(f.value.connectors[b]).label;E.value={text:`Remove the ${S} connector from ${f.value.name||f.value.id}?`,yes:"Remove",no:"Keep",run:()=>Yt(b)}},Ht=()=>he("/api/config").then(b=>{R.value="",t.value=(b.workspaces||[]).map(rt),a.value=b.connectors||[],n.value=b.fields,p.clear(),y.value=0,E.value=null,s.value=Math.max(0,t.value.findIndex(S=>S.id===k.workspace.id)),l.value=b.config_file||"",c.value=b.problems||[],h.value=b.tables||{},A.value=dt(t.value)}).catch(()=>{R.value=xe+"; settings cannot be loaded."}).then(kt).then(()=>{var b;g.settingsAt==="connectors"&&((b=m.value)==null||b.scrollIntoView({block:"start"})),g.settingsAt=""}),Ee=()=>{g.settings=!1},tt=()=>{E.value={text:"Discard changes?",yes:"Discard",no:"Keep editing",run:Ee}},st=()=>C.value?tt():Ee(),nt=b=>{const S=E.value;E.value=null,b&&(S==null||S.run())},Xt=async()=>{try{const b=await le("/api/config",{workspaces:t.value.map(S=>Ws(S)),secrets:js(t.value,p)});if(b.error){c.value=St(b.error);return}Ee(),It()}catch(b){c.value=[oe(b,{unreachable:xe+"; your changes are kept, save again."})]}};return K(C,b=>{g.settingsDirty=b},{immediate:!0}),K(()=>g.settingsAsk,b=>{b&&(g.settingsAsk=!1,tt())}),K(()=>g.settings,b=>{b&&Ht()}),(b,S)=>u(g).settings?(r(),i("div",{key:0,class:"modal",role:"dialog","aria-modal":"true","aria-labelledby":"settings-title",onClick:X(st,["self"])},[o("div",or,[o("div",ar,[S[2]||(S[2]=o("h3",{id:"settings-title",class:"modal__title"},"Workspaces",-1)),o("span",lr,_(l.value),1),C.value?(r(),i("span",rr,"Unsaved changes")):T("",!0),R.value?T("",!0):(r(),i("button",{key:1,type:"button",class:"btn btn--tonal",onClick:Xt},"Save")),o("button",{type:"button",class:"btn",onClick:st},"Close")]),E.value?(r(),i("div",ir,[o("span",cr,_(E.value.text),1),o("button",{type:"button",class:"btn btn--danger",onClick:S[0]||(S[0]=$=>nt(!0))},_(E.value.yes),1),o("button",{type:"button",class:"btn",onClick:S[1]||(S[1]=$=>nt(!1))},_(E.value.no),1)])):T("",!0),R.value?(r(),i("div",dr,_(R.value),1)):c.value.length?(r(),i("div",ur,_(c.value.join(" · ")),1)):T("",!0),R.value?T("",!0):(r(),i("div",pr,[o("ul",_r,[(r(!0),i(V,null,j(L.value,($,P)=>(r(),i("li",{key:$.uid},[o("button",{type:"button",class:"settings-sheet__workspace","aria-current":P===s.value,onClick:ve=>s.value=P},_($.label),9,hr)]))),128)),o("li",null,[o("button",{type:"button",class:"settings-sheet__workspace settings-sheet__workspace--add",onClick:Y}," + Add workspace ")])]),o("div",fr,[f.value?(r(),i(V,{key:1},[S[3]||(S[3]=o("h4",{class:"sheet-form__heading"},"Workspace",-1)),(r(!0),i(V,null,j(n.value.workspace,$=>(r(),q(ie,{key:$.key,spec:$,"model-value":Q(f.value,$.key),"onUpdate:modelValue":P=>u(re)(f.value,$.key,P)},null,8,["spec","model-value","onUpdate:modelValue"]))),128)),S[4]||(S[4]=o("h4",{class:"sheet-form__heading"},"Theme",-1)),(r(!0),i(V,null,j(n.value.theme,$=>{var P;return r(),q(ie,{key:$.key,spec:$,hint:$.key==="seed"&&!((P=f.value.theme)!=null&&P.seed)?ue(f.value.id):"","model-value":Q(f.value.theme,$.key),"onUpdate:modelValue":ve=>u(re)(f.value.theme,$.key,ve)},null,8,["spec","hint","model-value","onUpdate:modelValue"])}),128)),o("h4",vr,"Connectors",512),(r(!0),i(V,null,j(J.value,({entry:$,spec:P,uid:ve},Kt)=>(r(),i("div",{key:ve,class:"settings-sheet__connector"},[o("div",kr,[o("strong",yr,_(P.label),1),o("button",{type:"button",class:"btn btn--danger",onClick:ee=>Lt(Kt)}," Remove ",8,gr)]),O(ie,{spec:d,placeholder:P.type,hint:"only needed when one type appears twice","model-value":Q($,"id"),"onUpdate:modelValue":ee=>u(re)($,"id",ee)},null,8,["placeholder","model-value","onUpdate:modelValue"]),O(ie,{spec:v,placeholder:P.label,hint:"shown in the feed filter","model-value":Q($,"label"),"onUpdate:modelValue":ee=>u(re)($,"label",ee)},null,8,["placeholder","model-value","onUpdate:modelValue"]),(r(!0),i(V,null,j(P.fields,ee=>(r(),q(ie,{key:ee.key,spec:ee,"model-value":Q($,ee.key),"onUpdate:modelValue":Oe=>u(re)($,ee.key,Oe),onSecret:(Oe,Gt)=>x($,Oe,Gt)},null,8,["spec","model-value","onUpdate:modelValue","onSecret"]))),128))]))),128)),o("div",wr,[(r(!0),i(V,null,j(a.value,$=>(r(),i("button",{key:$.type,type:"button",class:"btn",onClick:P=>Re($)}," + "+_($.label),9,br))),128))]),S[5]||(S[5]=o("h4",{class:"sheet-form__heading"},"Rules",-1)),(r(!0),i(V,null,j(n.value.rules,$=>(r(),q(ie,{key:$.key,spec:$,"model-value":Q(f.value.rules,$.key),"onUpdate:modelValue":P=>u(re)(f.value.rules,$.key,P)},null,8,["spec","model-value","onUpdate:modelValue"]))),128)),(r(!0),i(V,null,j(n.value.statuses,$=>(r(),q(ie,{key:$.key,spec:$,"model-value":Q(f.value.rules.statuses,$.key),"onUpdate:modelValue":P=>u(re)(f.value.rules.statuses,$.key,P)},null,8,["spec","model-value","onUpdate:modelValue"]))),128)),S[6]||(S[6]=o("h4",{class:"sheet-form__heading"},"Whose turn table",-1)),S[7]||(S[7]=o("p",{class:"settings-sheet__rules-note"}," The rows in force, first match wins. Read-only here: ask Claude to change rules.table through /api/config. ",-1)),B.value.length?(r(),i("ol",xr,[(r(!0),i(V,null,j(B.value,($,P)=>(r(),i("li",{key:P,class:"settings-sheet__rule"},[o("span",$r,_($.when),1),o("span",Cr,_($.then),1)]))),128))])):(r(),i("p",Sr,"Save the workspace to see its rows.")),o("div",{class:"settings-sheet__actions"},[o("button",{type:"button",class:"btn btn--danger",onClick:Z}," Delete workspace ")])],64)):(r(),i("p",mr,"No workspaces yet. Add one on the left."))])]))])])):T("",!0)}}),Mr=F(Tr,[["__scopeId","data-v-779d567d"]]),Ar={class:"app__shell"},Rr=["inert"],Er={id:"wt",class:"dashboard dashboard--fit"},Or=["inert"],Dr={class:"dashboard__main"},Ir=N({__name:"App",setup(e){const t=w(()=>!!k.workspace.id&&!k.workspace.connectors.length),s=w(()=>Dt.value!==null);return K(ut,Is,{flush:"post"}),hs("Escape",Zs,{target:document}),ts(()=>{Gs()}),(a,n)=>(r(),i("div",{class:"app",style:ss(u(ut))},[O(Cn,{inert:s.value,onSettings:n[0]||(n[0]=l=>u(g).settings=!0)},null,8,["inert"]),o("div",Ar,[O(Nn,{inert:s.value,onReport:n[1]||(n[1]=l=>u(g).feedback=!0)},null,8,["inert"]),u(pt).length?(r(),i("div",{key:0,class:"alert",inert:s.value},_(u(pt).join(" · ")),9,Rr)):T("",!0),o("div",Er,[n[3]||(n[3]=o("canvas",{id:"backdrop",class:"dashboard__backdrop","aria-hidden":"true"},null,-1)),O(uo,{inert:s.value},null,8,["inert"]),t.value?(r(),q(ml,{key:0,inert:s.value},null,8,["inert"])):(r(),i("div",{key:1,class:"dashboard__layout",inert:s.value},[o("div",Dr,[O(ta,{class:"dashboard__move"}),O(_a,{class:"dashboard__todo"}),O(ba,{class:"dashboard__waiting"})]),O(dl,{class:"dashboard__feed",onOpen:n[2]||(n[2]=l=>u(g).modal=l)})],8,Or)),O(Ll),O(Mr),O(Al)])])],4))}}),Br=F(Ir,[["__scopeId","data-v-f146b085"]]);ns(Br).mount("#app");nn();Bs();
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.rail[data-v-be863916]{position:fixed;inset:0 auto 0 0;z-index:30;box-sizing:border-box;display:flex;flex-direction:column;gap:4px;width:60px;padding:12px 8px;overflow:hidden;border-right:1px solid var(--glass-edge);background:rgb(var(--glass),.72);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);font-family:var(--md-font);color:var(--md-on-surface);transition:width .18s ease,box-shadow .18s ease}.rail[data-v-be863916]:hover,.rail[data-v-be863916]:has(:focus-visible){width:232px;box-shadow:12px 0 32px #0000001f}.app:has(.modal) .rail[data-v-be863916]{z-index:0}.rail__item[data-v-be863916]{position:relative;display:flex;align-items:center;gap:12px;height:44px;padding:0 4px;border-radius:12px;color:inherit;text-decoration:none;white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none}.rail__item[data-v-be863916]:focus-visible{outline:var(--focus-ring);outline-offset:-2px}.rail__item--dragging[data-v-be863916]{opacity:.4}.rail__item[data-drop=before][data-v-be863916]{box-shadow:0 -2px 0 0 var(--md-primary)}.rail__item[data-drop=after][data-v-be863916]{box-shadow:0 2px 0 0 var(--md-primary)}.rail__item[data-v-be863916]:hover{background:var(--state-hover)}.rail__item[aria-selected=true][data-v-be863916]{background:var(--md-secondary-container);color:var(--md-on-secondary-container)}.rail__icon[data-v-be863916]{flex:none;display:block;width:36px;height:36px;border-radius:10px}.rail__gear[data-v-be863916]{flex:none;display:grid;place-items:center;width:36px;height:36px;color:var(--md-on-surface-variant)}.rail__label[data-v-be863916]{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:14px;font-weight:500;opacity:0;transition:opacity .12s ease}.rail:hover .rail__label[data-v-be863916],.rail:has(:focus-visible) .rail__label[data-v-be863916]{opacity:1}.rail__badge[data-v-be863916]{position:absolute;top:2px;left:30px;box-sizing:border-box;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--md-primary);color:var(--md-on-primary);font-size:10px;font-weight:700;line-height:18px;text-align:center}.rail:hover .rail__badge[data-v-be863916],.rail:has(:focus-visible) .rail__badge[data-v-be863916]{position:static;flex:none}.rail__spacer[data-v-be863916]{flex:1}.ai-button[data-v-d32a60c7]{display:inline-grid;place-items:center;width:26px;height:26px;border-radius:50%;cursor:pointer;color:var(--md-primary);background:rgb(var(--md-state),.06);vertical-align:middle;transition:background .12s ease,transform .12s ease}.ai-button__icon[data-v-d32a60c7]{width:15px;height:15px}.ai-button[data-v-d32a60c7]:hover{background:var(--md-secondary-container);transform:scale(1.08)}.ai-button[data-v-d32a60c7]:focus-visible{outline:var(--focus-ring);outline-offset:1px}.ai-button.ai-button--sent[data-v-d32a60c7]{background:var(--md-tertiary-container);color:var(--md-on-tertiary-container)}.ai-button.ai-button--failed[data-v-d32a60c7]{background:var(--md-error-container);color:var(--md-on-error-container)}.ai-button--big[data-v-d32a60c7]{width:32px;height:32px;background:rgb(var(--glass),.7)}.ai-button--big .ai-button__icon[data-v-d32a60c7]{width:18px;height:18px}.status-bar[data-v-b74e71cb]{display:flex;align-items:center;gap:10px;margin:0 4px 8px;font-family:var(--md-font);font-size:12px;color:var(--md-on-surface-variant)}.status-bar__btn[data-v-b74e71cb]{height:26px;padding:0 12px;font-size:12px;text-decoration:none}.status-bar__btn--busy[data-v-b74e71cb]{opacity:.6;pointer-events:none}.status-bar__report[data-v-b74e71cb]{gap:6px;color:var(--md-error);border-color:var(--md-error)}.status-bar__icon[data-v-b74e71cb]{width:16px;height:16px}.status-bar__live[data-v-b74e71cb]{width:8px;height:8px;border-radius:50%;background:var(--live-on);box-shadow:0 0 0 0 var(--live-pulse-rest);animation:status-bar-pulse-b74e71cb 2s infinite}.status-bar__live--off[data-v-b74e71cb]{background:var(--live-off);animation:none}.status-bar__live--warn[data-v-b74e71cb]{background:var(--live-warn);animation:none}.status-bar__notice[data-v-b74e71cb]{color:var(--live-off)}.status-bar__ai-hint[data-v-b74e71cb]{margin-left:-4px}@keyframes status-bar-pulse-b74e71cb{0%{box-shadow:0 0 0 0 var(--live-pulse-from)}70%{box-shadow:0 0 0 8px var(--live-pulse-to)}to{box-shadow:0 0 0 0 var(--live-pulse-to)}}@media(prefers-reduced-motion:reduce){.status-bar__live[data-v-b74e71cb]{animation:none}}.workspace-header[data-v-0f4b6d6e]{display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,2fr) 32px;gap:8px;padding:8px;background:linear-gradient(135deg,var(--blob-1),var(--blob-2));color:var(--header-ink, var(--md-on-primary-container))}@media(prefers-reduced-transparency:reduce){.workspace-header[data-v-0f4b6d6e]{background:var(--md-s-low)}}.workspace-header__title[data-v-0f4b6d6e]{display:grid;align-content:center;gap:4px;padding:12px 16px}.workspace-header__name[data-v-0f4b6d6e]{margin:0;font-size:28px;font-weight:400;line-height:36px;color:var(--md-on-primary-container)}.workspace-header__line[data-v-0f4b6d6e]{margin:0;font-size:14px;opacity:.85}.workspace-header__count[data-v-0f4b6d6e]{font-weight:700;font-variant-numeric:tabular-nums}.workspace-header__give[data-v-0f4b6d6e]{display:flex;align-items:center;gap:8px;margin-top:8px}.workspace-header__links[data-v-0f4b6d6e]{display:inline-flex;flex-wrap:wrap;gap:6px;margin-left:10px}.workspace-header__links a[data-v-0f4b6d6e]{display:inline-flex;align-items:center;height:24px;padding:0 10px;border:1px solid var(--glass-edge);border-radius:999px;background:rgb(var(--glass),.55);color:inherit;font-size:12px;font-weight:500}.workspace-header__links a[data-v-0f4b6d6e]:hover{background:rgb(var(--glass),.85)}.workspace-header__fold[data-v-0f4b6d6e]{grid-row:1;grid-column:3;place-self:center center;margin:0;color:inherit}.workspace-header__tiles[data-v-0f4b6d6e]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}.workspace-header__tile[data-v-0f4b6d6e]{display:grid;align-content:start;gap:4px;padding:12px 16px;border:1px solid var(--glass-edge);border-radius:var(--md-r-card);background:rgb(var(--glass),calc(var(--glass-a) + .1));-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);color:var(--md-on-surface)}@media(prefers-reduced-transparency:reduce){.workspace-header__tile[data-v-0f4b6d6e]{background:var(--md-s-low);-webkit-backdrop-filter:none;backdrop-filter:none}}.workspace-header__tile-label[data-v-0f4b6d6e]{color:var(--md-on-surface-variant);font-size:12px;font-weight:500;line-height:16px;letter-spacing:.03em}.workspace-header__tile-value[data-v-0f4b6d6e]{min-height:40px;font-size:32px;font-weight:400;line-height:40px;font-variant-numeric:tabular-nums}.workspace-header__tile--you[data-v-0f4b6d6e]{border-color:transparent;background:var(--md-primary);color:var(--md-on-primary)}.workspace-header__tile--you .workspace-header__tile-label[data-v-0f4b6d6e]{color:var(--md-on-primary);opacity:.85}.workspace-header__tile--fail .workspace-header__tile-value[data-v-0f4b6d6e]{color:var(--md-error)}.workspace-header--collapsed[data-v-0f4b6d6e]{grid-template-columns:minmax(0,1fr) 32px;padding:4px 8px}.workspace-header--collapsed .workspace-header__title[data-v-0f4b6d6e]{display:flex;flex-wrap:wrap;align-items:baseline;gap:14px;padding:4px 8px}.workspace-header--collapsed .workspace-header__name[data-v-0f4b6d6e]{font-size:18px;line-height:28px}.workspace-header--collapsed .workspace-header__tiles[data-v-0f4b6d6e],.workspace-header--collapsed .workspace-header__give[data-v-0f4b6d6e],.workspace-header--collapsed .workspace-header__sentence[data-v-0f4b6d6e]{display:none}.workspace-header--collapsed .workspace-header__fold[data-v-0f4b6d6e]{grid-column:2}.workspace-header--collapsed .workspace-header__strip[data-v-0f4b6d6e]{font-size:13px}@media(max-width:900px){.workspace-header[data-v-0f4b6d6e],.workspace-header--collapsed[data-v-0f4b6d6e]{grid-template-columns:minmax(0,1fr) 32px}.workspace-header__fold[data-v-0f4b6d6e]{grid-column:2}.workspace-header__tiles[data-v-0f4b6d6e]{grid-column:1 / -1}}@media(max-width:640px){.workspace-header__tiles[data-v-0f4b6d6e]{grid-template-columns:repeat(2,minmax(0,1fr))}}.island__head[data-v-a03b7f62]{display:flex;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:16px;padding:12px 20px}.island__title[data-v-a03b7f62]{flex:none;margin:0;font-size:16px;line-height:24px;font-weight:500;letter-spacing:.01em;white-space:nowrap}.island__note[data-v-a03b7f62]{display:flex;flex:1 1 auto;align-items:center;justify-content:flex-end;gap:6px;min-width:0;margin:0;overflow:visible;color:var(--md-on-surface-variant);font-size:12px;line-height:16px;letter-spacing:.03em;text-align:right;white-space:nowrap;text-overflow:ellipsis}.island__note-text[data-v-a03b7f62]{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.island__body[data-v-a03b7f62]{padding:0 20px 12px}.island--column .island__head[data-v-a03b7f62]{gap:4px}.island--column .island__body[data-v-a03b7f62]{display:flex;flex-direction:column;min-height:0;padding-top:4px}.island.island--collapsed .island__body[data-v-a03b7f62]{display:none}@media(min-width:901px){.dashboard--fit .dashboard__main .island__body[data-v-a03b7f62]{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;padding-bottom:0}.dashboard--fit .dashboard__main .island.island--collapsed .island__body[data-v-a03b7f62]{display:none}}.segmented[data-v-7901b5d6]{display:inline-flex;overflow:hidden;border:1px solid var(--md-outline);border-radius:999px;vertical-align:middle}.segmented--chips[data-v-7901b5d6]{flex:none;align-self:flex-start;background:rgb(var(--glass),.4)}.segmented__option[data-v-7901b5d6]{display:inline-flex;align-items:center;gap:5px;height:26px;padding:0 11px;border-left:1px solid var(--md-outline);font-size:12px;font-weight:500;color:var(--md-on-surface);cursor:pointer;-webkit-user-select:none;user-select:none}.segmented__option[data-v-7901b5d6]:first-child{border-left:0}.segmented__count[data-v-7901b5d6]{font-weight:700;font-variant-numeric:tabular-nums}.segmented__option[aria-pressed=true][data-v-7901b5d6]{background:var(--md-secondary-container);color:var(--md-on-secondary-container)}.segmented__option[data-v-7901b5d6]:focus-visible{outline:var(--focus-ring);outline-offset:-2px}.segmented--chips .segmented__option[data-v-7901b5d6]{gap:normal;height:32px;padding:0 14px;font-family:var(--md-font);font-size:14px;letter-spacing:.01em}.segmented--chips .segmented__option[data-v-7901b5d6]:hover{background:var(--state-hover)}.segmented--chips .segmented__option[aria-pressed=true][data-v-7901b5d6]{background:var(--md-secondary-container);color:var(--md-on-secondary-container)}.ticket-cell__summary[data-v-8436de88]{display:block;max-width:44ch;margin-top:2px;color:var(--md-on-surface-variant);font-size:12px;line-height:16px;letter-spacing:.03em}.pr-cell[data-v-75c443c3]{display:flex;flex-direction:column;align-items:flex-start;gap:4px}.pr-cell__link[data-v-75c443c3]{max-width:100%;overflow-wrap:anywhere}.recently-done[data-v-12ee20e8]{display:flex;flex:0 1 auto;flex-direction:column;min-height:33px;margin:0 -20px;padding:0 20px;overflow:hidden;border-top:1px solid var(--md-outline-variant)}.recently-done[open][data-v-12ee20e8]{min-height:128px}.recently-done__summary[data-v-12ee20e8]{display:flex;flex:none;align-items:center;gap:4px;min-height:40px;list-style:none;color:var(--md-on-surface-variant);font-size:14px;font-weight:500;cursor:pointer}.recently-done__summary[data-v-12ee20e8]::-webkit-details-marker{display:none}.recently-done__summary[data-v-12ee20e8]:before{content:"▾";display:inline-grid;place-items:center;width:24px;font-size:18px;transform:rotate(-90deg);transition:transform .15s ease}.recently-done[open] .recently-done__summary[data-v-12ee20e8]:before{transform:none}.recently-done__count[data-v-12ee20e8]{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;margin-left:6px;padding:0 6px;border-radius:10px;background:var(--md-s-high);color:var(--md-on-surface-variant);font-size:12px;font-weight:700}.recently-done__list[data-v-12ee20e8]{flex:1 1 auto;min-height:0;max-height:min(30vh,240px);margin:0 -20px;padding-bottom:8px;overflow:auto}.row-scroll[data-v-7bd9ace7]{overflow-x:auto;margin:0 -20px}@media(min-width:901px){.dashboard--fit .dashboard__main .row-scroll[data-v-7bd9ace7]{flex:1 1 auto;min-height:0;overflow:auto}.dashboard--fit .row-scroll[data-v-7bd9ace7]::-webkit-scrollbar{width:8px;height:8px}.dashboard--fit .row-scroll[data-v-7bd9ace7]::-webkit-scrollbar-thumb{border-radius:4px;background:var(--md-outline-variant)}}.dashboard--fit .dashboard__main .row-scroll--floor[data-v-7bd9ace7]{min-height:5rem}.row-scroll__more[data-v-7bd9ace7]{flex:none;display:block;margin:0 -20px;padding:4px 20px 6px;border-top:1px solid var(--md-outline-variant);color:var(--md-primary);font-size:13px;font-weight:500;cursor:pointer}.row-scroll__more[data-v-7bd9ace7]:hover{background:rgb(var(--md-state),.06)}.row-scroll__more[data-v-7bd9ace7]:focus-visible{outline:var(--focus-ring);outline-offset:-2px}.your-move__view[data-v-e3e1502a]{margin-left:10px}.your-move__next[data-v-e3e1502a]{font-weight:500}.your-move__quote[data-v-e3e1502a]{max-width:48ch;color:var(--md-on-surface-variant);font-size:12px;line-height:16px}.your-move__quote a[data-v-e3e1502a]{color:var(--md-on-surface-variant)}.your-move__problem[data-v-e3e1502a]{margin:4px 0 0;padding:4px 10px;font-size:12px}.your-move__mark[data-v-e3e1502a]{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 12px;border:1px solid var(--md-outline);border-radius:999px;color:var(--md-on-surface);font-size:12px;font-weight:600;white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none}.your-move__mark[data-v-e3e1502a]:hover{background:var(--state-hover)}.your-move__mark[data-v-e3e1502a]:before{content:"";width:9px;height:5px;border-bottom:2px solid currentcolor;border-left:2px solid currentcolor;transform:translateY(-2px) rotate(-45deg)}.your-move__mark--undo[data-v-e3e1502a]:before{display:none}.your-move__mark[data-v-e3e1502a]:focus-visible{outline:var(--focus-ring);outline-offset:2px}.your-move__row--handled td[data-v-e3e1502a]{opacity:.55}.your-move__row--handled .your-move__next[data-v-e3e1502a]{text-decoration:line-through;text-decoration-color:var(--md-outline)}.your-move__row--handled .table__actions[data-v-e3e1502a]{opacity:1}.todos__view[data-v-6f034e1e]{margin-left:10px}.todos__add[data-v-6f034e1e]{display:flex;align-items:center;gap:8px;padding:8px 0}.todos__input[data-v-6f034e1e]{flex:1;min-width:0;height:32px;padding:0 12px;background:rgb(var(--md-state),.04);font-size:14px}.todos__list[data-v-6f034e1e]{margin:0 -20px;padding:0;overflow-y:auto;list-style:none}.todos__item[data-v-6f034e1e]{display:grid;grid-template-columns:20px minmax(0,1fr) auto 20px;align-items:center;gap:10px;padding:7px 20px;border-top:1px solid var(--md-outline-variant)}.todos__item[data-v-6f034e1e]:first-child{border-top:0}.todos__item[data-v-6f034e1e]:hover{background:rgb(var(--md-state),.06)}.todos__text[data-v-6f034e1e]{overflow-wrap:anywhere}.todos__item--done .todos__text[data-v-6f034e1e]{color:var(--md-on-surface-variant);text-decoration:line-through}.todos__empty[data-v-6f034e1e]{display:block;padding:6px 20px 10px}.todos__check[data-v-6f034e1e]{box-sizing:border-box;display:grid;place-items:center;width:18px;height:18px;border:2px solid var(--md-outline);border-radius:5px;cursor:pointer}.todos__check--on[data-v-6f034e1e]{border-color:var(--md-primary);background:var(--md-primary)}.todos__check--on[data-v-6f034e1e]:after{content:"";width:8px;height:4px;border-bottom:2px solid var(--md-on-primary);border-left:2px solid var(--md-on-primary);transform:translateY(-1px) rotate(-45deg)}.todos__meta[data-v-6f034e1e]{color:var(--md-on-surface-variant);font-size:11px;white-space:nowrap}.todos__delete[data-v-6f034e1e]{color:var(--md-on-surface-variant);font-size:16px;line-height:1;text-align:center;cursor:pointer;opacity:0}.todos__delete[data-v-6f034e1e]:focus-visible{opacity:1}.todos__item:hover .todos__delete[data-v-6f034e1e]{opacity:1}@media(min-width:901px){.dashboard--fit .dashboard__todo .todos__list[data-v-6f034e1e]{flex:1 1 auto;min-height:0}.dashboard--fit .dashboard__todo .todos__list[data-v-6f034e1e]:not(:has(.todos__item)){display:none}}.waiting__table[data-v-a632447c]{table-layout:fixed}.waiting__col--ticket[data-v-a632447c]{width:22%}.waiting__col--status[data-v-a632447c],.waiting__col--with[data-v-a632447c]{width:6.5rem}.waiting__col--pr[data-v-a632447c]{width:4.5rem}.waiting__col--actions[data-v-a632447c]{width:2rem}.waiting__reason[data-v-a632447c]{display:block;min-width:0;max-width:44ch;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.feed-item[data-v-27e556e1]{display:grid;grid-template-columns:32px minmax(0,1fr);align-items:start;gap:12px;padding:10px 20px;cursor:default}.feed-item[data-v-27e556e1]:hover{background:var(--state-hover)}.feed-item.feed-item--open[data-v-27e556e1]{background:rgb(var(--md-state),.05)}.feed-item.feed-item--new[data-v-27e556e1]{background:var(--new-bg);box-shadow:inset 4px 0 0 var(--new),inset 0 0 0 1px var(--new-edge)}.feed-item__icon[data-v-27e556e1]{flex:none;display:grid;place-items:center;width:32px;height:32px;border-radius:50%;background:var(--md-s-highest);color:var(--md-on-surface-variant);font-size:11px;font-weight:700;letter-spacing:.02em}.feed-item__icon--jira[data-v-27e556e1]{background:var(--md-primary-container);color:var(--md-on-primary-container)}.feed-item__icon--bad[data-v-27e556e1]{background:var(--md-error-container);color:var(--md-on-error-container)}.feed-item__icon--ok[data-v-27e556e1]{background:var(--md-tertiary-container);color:var(--md-on-tertiary-container)}.feed-item__body[data-v-27e556e1]{display:grid;gap:2px;min-width:0}.feed-item__top[data-v-27e556e1]{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;font-size:14px}.feed-item__right[data-v-27e556e1]{flex:none;display:inline-flex;align-items:center;gap:4px;margin-left:auto}.feed-item__kind[data-v-27e556e1]{font-weight:500}.feed-item__kind--bad[data-v-27e556e1]{color:var(--md-error)}.feed-item__who[data-v-27e556e1]{color:var(--md-on-surface-variant)}.feed-item__text[data-v-27e556e1]{display:-webkit-box;overflow:hidden;color:var(--md-on-surface-variant);font-size:12px;line-height:16px;letter-spacing:.03em;overflow-wrap:anywhere;-webkit-line-clamp:2;-webkit-box-orient:vertical}.feed-item__toggle[data-v-27e556e1]{display:inline-grid;place-items:center;width:24px;height:24px;margin-left:2px;border-radius:50%;color:var(--md-on-surface-variant);font-size:0;line-height:1;cursor:pointer;-webkit-user-select:none;user-select:none;transition:transform .15s ease}.feed-item__toggle[data-v-27e556e1]:before{content:"";width:7px;height:7px;border-right:2px solid currentcolor;border-bottom:2px solid currentcolor;transform:translateY(-2px) rotate(45deg)}.feed-item__toggle[data-v-27e556e1]:hover{background:rgb(var(--md-state),.1)}.feed-item__toggle[aria-expanded=true][data-v-27e556e1]{transform:rotate(180deg)}.feed-item__toggle[data-v-27e556e1]:focus-visible{outline:var(--focus-ring)}.feed-item__full[data-v-27e556e1]{max-height:16rem;margin-top:6px;padding:10px 12px;overflow:auto;border:1px solid var(--glass-edge);border-radius:var(--md-r-chip);background:rgb(var(--glass),.55);color:var(--md-on-surface);font-size:13px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}.feed-item__actions[data-v-27e556e1]{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}.feed-item--new .feed-item__kind[data-v-27e556e1],.feed-item--lit .feed-item__kind[data-v-27e556e1]{color:var(--md-on-surface);font-weight:700}.feed-item--new .feed-item__text[data-v-27e556e1],.feed-item--lit .feed-item__text[data-v-27e556e1]{color:var(--md-on-surface)}.feed-item--new .feed-item__icon[data-v-27e556e1],.feed-item--lit .feed-item__icon[data-v-27e556e1]{box-shadow:0 0 0 2px var(--new)}.feed-item--new .feed-item__when[data-v-27e556e1]:before,.feed-item--lit .feed-item__when[data-v-27e556e1]:before{content:"NEW";display:inline-block;margin-right:6px;padding:1px 6px;border-radius:4px;background:var(--new);color:var(--new-ink);font-size:10px;font-weight:800;letter-spacing:.06em;vertical-align:1px}.feed-item:not(.feed-item--new) .feed-item__body[data-v-27e556e1]{opacity:.78}.feed__bar[data-v-d92a7c74]{display:flex;flex:none;flex-wrap:nowrap;align-items:center;justify-content:space-between;gap:8px;margin:0 0 8px}.feed__new-count[data-v-d92a7c74]{display:inline-flex;align-items:center;height:22px;padding:0 9px;border-radius:999px;background:var(--new);color:var(--new-ink);box-shadow:0 0 12px var(--new-glow);font-size:12px;font-weight:800}.feed__mark-read[data-v-d92a7c74]{flex:none;padding:4px 8px;border-radius:999px;color:var(--md-primary);font-size:13px;font-weight:500;cursor:pointer;-webkit-user-select:none;user-select:none}.feed__mark-read[data-v-d92a7c74]:hover{background:var(--state-hover)}.feed__mark-read[data-v-d92a7c74]:focus-visible{outline:var(--focus-ring)}.feed__search[data-v-d92a7c74]{display:flex;flex:none;align-items:center;gap:8px;margin:0 0 8px}.feed__search-input[data-v-d92a7c74]{flex:1;min-width:0;height:30px;padding:0 10px;font-size:13px}.feed__search-count[data-v-d92a7c74]{flex:none;color:var(--md-on-surface-variant);font-size:12px}.feed__list[data-v-d92a7c74]{flex:1 1 auto;min-height:12rem;margin:0 -20px;padding:0 0 4px;overflow-y:auto;list-style:none}.feed__group[data-v-d92a7c74]{display:grid;grid-template-columns:minmax(0,1fr);align-items:start;gap:0;padding:0;cursor:default}.feed__group[data-v-d92a7c74]:hover{background:var(--state-hover)}.feed__group.feed__group--open[data-v-d92a7c74]{background:rgb(var(--md-state),.05)}.feed__group.feed__group--new[data-v-d92a7c74]{background:var(--new-bg);box-shadow:inset 4px 0 0 var(--new),inset 0 0 0 1px var(--new-edge)}.feed__group-head[data-v-d92a7c74]{display:grid;grid-template-columns:32px minmax(0,1fr) auto;align-items:center;gap:12px;padding:10px 20px;cursor:pointer}.feed__group-copy[data-v-d92a7c74]{opacity:.55}.feed__group-head:hover .feed__group-copy[data-v-d92a7c74]{opacity:1}.feed__group-head[data-v-d92a7c74]:hover{background:var(--state-hover)}.feed__group--new>.feed__group-head[data-v-d92a7c74]{background:var(--new-bg);box-shadow:inset 4px 0 0 var(--new)}.feed__group--move[data-v-d92a7c74],.feed__group--move>.feed__group-head[data-v-d92a7c74]{box-shadow:inset 4px 0 0 var(--move)}.feed__group.feed__group--new.feed__group--move[data-v-d92a7c74]{box-shadow:inset 4px 0 0 var(--move),inset 0 0 0 1px var(--new-edge)}.feed__group-text[data-v-d92a7c74]{min-width:0}.feed__group-key[data-v-d92a7c74]{display:flex;align-items:baseline;gap:8px;font-size:14px}.feed__group-ticket[data-v-d92a7c74]{flex:none;font-weight:600}.feed__group-sum[data-v-d92a7c74]{overflow:hidden;color:var(--md-on-surface-variant);font-size:13px;text-overflow:ellipsis;white-space:nowrap}.feed__group-meta[data-v-d92a7c74]{display:flex;align-items:center;gap:8px;margin-top:2px;color:var(--md-on-surface-variant);font-size:12px}.feed__group-kinds[data-v-d92a7c74]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.feed__group-when[data-v-d92a7c74]{margin-left:auto}.feed__group-count[data-v-d92a7c74]{flex:none;display:inline-flex;align-items:center;height:18px;padding:0 7px;border-radius:999px;background:var(--md-s-highest);color:var(--md-on-surface-variant);font-size:11px;font-weight:700}.feed__group-count--new[data-v-d92a7c74]{background:var(--new);color:var(--new-ink)}.feed__children[data-v-d92a7c74]{margin:0 0 0 35px;padding:0 0 4px;border-left:2px solid var(--md-outline-variant);list-style:none}.feed__children .feed-item[data-v-d92a7c74]{padding-left:14px}.feed__group-caret[data-v-d92a7c74]{flex:none;display:grid;place-items:center;width:32px;height:32px;border-radius:50%;background:var(--md-s-highest);color:var(--md-on-surface-variant);font-size:14px;transition:transform .15s ease}.feed__group-caret[data-v-d92a7c74]:focus-visible{outline:var(--focus-ring)}.feed__group--open .feed__group-caret[data-v-d92a7c74]{transform:rotate(90deg)}@media(min-width:901px){.dashboard--fit .feed__list[data-v-d92a7c74]::-webkit-scrollbar{width:8px;height:8px}.dashboard--fit .feed__list[data-v-d92a7c74]::-webkit-scrollbar-thumb{border-radius:4px;background:var(--md-outline-variant)}}@media(max-width:900px){.feed__list[data-v-d92a7c74]{max-height:28rem}}.first-run[data-v-ef746675]{display:grid;place-self:start center;gap:14px;width:min(640px,100%);padding:28px 32px}.first-run__title[data-v-ef746675]{margin:0;font-size:22px;font-weight:400;line-height:28px}.first-run__text[data-v-ef746675]{margin:0;color:var(--md-on-surface-variant)}.first-run__actions[data-v-ef746675]{display:flex;flex-wrap:wrap;gap:8px;margin-top:4px}.first-run__ai[data-v-ef746675]{font-size:13px}.markdown-view[data-v-dcf42e95]{padding:16px 20px 20px;overflow:auto;overflow-wrap:anywhere;font-size:14px;line-height:1.6}.markdown-view[data-v-dcf42e95] h4,.markdown-view[data-v-dcf42e95] h5{margin:16px 0 6px;font-weight:600}.markdown-view[data-v-dcf42e95] h4{font-size:16px}.markdown-view[data-v-dcf42e95] h5{font-size:14px}.markdown-view[data-v-dcf42e95] p{margin:0 0 10px}.markdown-view[data-v-dcf42e95] ul,.markdown-view[data-v-dcf42e95] ol{margin:0 0 10px;padding-left:22px}.markdown-view[data-v-dcf42e95] li{margin:2px 0}.markdown-view[data-v-dcf42e95] code{padding:1px 5px;border-radius:4px;background:rgb(var(--md-state),.1);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}.markdown-view[data-v-dcf42e95] pre{margin:0 0 12px;padding:12px 14px;overflow:auto;border-radius:8px;background:#00000047}.markdown-view[data-v-dcf42e95] pre code{padding:0;background:none;font-size:12.5px;line-height:1.5;white-space:pre}.markdown-view[data-v-dcf42e95] blockquote{margin:0 0 10px;padding:4px 12px;border-left:3px solid var(--md-primary);color:var(--md-on-surface-variant)}.markdown-view[data-v-dcf42e95] strong{font-weight:600}.markdown-view[data-v-dcf42e95] .markdown-view__mention{color:var(--md-primary);font-weight:500}.feedback-sheet__card[data-v-ec34adcc]{width:min(640px,100%)}.feedback-sheet__text[data-v-ec34adcc]{min-height:7rem;resize:vertical}.feedback-sheet__note[data-v-ec34adcc]{margin:0;color:var(--md-on-surface-variant);font-size:12px}.feedback-sheet__report[data-v-ec34adcc]{display:flex;align-items:baseline;gap:8px;padding:4px 0;border-top:1px solid var(--md-outline-variant);font-size:13px}.feedback-sheet__report-title[data-v-ec34adcc]{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.field-row[data-v-2ba0ba86]{display:grid;grid-template-columns:170px minmax(0,1fr);align-items:center;gap:4px 14px}.field-row__label[data-v-2ba0ba86]{color:var(--md-on-surface);font-size:13px}.field-row__hint[data-v-2ba0ba86]{grid-column:2;color:var(--md-on-surface-variant);font-size:11px}.field-row__check[data-v-2ba0ba86]{display:flex;align-items:center;gap:8px;color:var(--md-on-surface-variant);font-size:11px}.field-row__colour[data-v-2ba0ba86]{width:44px;height:30px;padding:2px}.field-row__inline[data-v-2ba0ba86]{display:flex;align-items:center;gap:8px}.settings-sheet__card[data-v-779d567d]{width:min(1040px,100%);height:min(760px,100%)}.settings-sheet__dirty[data-v-779d567d]{color:var(--md-error);font-size:12px;font-weight:600}.settings-sheet__confirm[data-v-779d567d]{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:12px 20px 0;padding:8px 12px;border-radius:10px;background:var(--md-warn-container);color:var(--md-on-warn-container);font-size:13px}.settings-sheet__confirm-text[data-v-779d567d]{flex:1}.settings-sheet__alert[data-v-779d567d]{margin:12px 20px 0}.settings-sheet__body[data-v-779d567d]{display:grid;flex:1;grid-template-columns:220px minmax(0,1fr);gap:0;min-height:0}.settings-sheet__list[data-v-779d567d]{display:flex;flex-direction:column;gap:4px;margin:0;padding:12px;overflow:auto;border-right:1px solid var(--md-outline-variant);list-style:none}.settings-sheet__workspace[data-v-779d567d]{box-sizing:border-box;display:block;width:100%;padding:8px 12px;overflow:hidden;border-radius:10px;font-size:14px;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.settings-sheet__workspace[data-v-779d567d]:hover{background:var(--state-hover)}.settings-sheet__workspace[aria-current=true][data-v-779d567d]{background:var(--md-secondary-container);color:var(--md-on-secondary-container)}.settings-sheet__workspace[data-v-779d567d]:focus-visible{outline:var(--focus-ring);outline-offset:-2px}.settings-sheet__workspace--add[data-v-779d567d]{color:var(--md-primary);font-weight:500}.settings-sheet__connector[data-v-779d567d]{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--md-outline-variant);border-radius:12px;background:rgb(var(--glass),.35)}.settings-sheet__connector-head[data-v-779d567d]{display:flex;align-items:center;gap:10px}.settings-sheet__connector-name[data-v-779d567d]{flex:1;font-weight:600}.settings-sheet__actions[data-v-779d567d]{display:flex;flex-wrap:wrap;align-items:center;gap:10px}.settings-sheet__rules[data-v-779d567d]{display:flex;flex-direction:column;gap:6px;margin:0;padding:0 0 0 28px;font-size:13px}.settings-sheet__rule[data-v-779d567d]{display:grid;grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:12px;padding:4px 0;border-bottom:1px solid var(--md-outline-variant)}.settings-sheet__rule-when[data-v-779d567d]{color:var(--md-on-surface-variant);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;overflow-wrap:anywhere}.settings-sheet__rule-then[data-v-779d567d]{overflow-wrap:anywhere}.settings-sheet__rules-note[data-v-779d567d]{margin:0;color:var(--md-on-surface-variant)}.app__shell[data-v-f146b085]{box-sizing:border-box;display:flex;flex-direction:column;height:100%;padding:12px 12px 12px 72px}.app__shell>.dashboard[data-v-f146b085]{flex:1 1 auto;min-height:0}@media(min-width:901px){.app__shell>.dashboard.dashboard--fit[data-v-f146b085]{height:auto}}@media(max-width:900px){.app__shell[data-v-f146b085]{height:auto}}
|