@timqi/pier 0.0.20 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config-sync-fetch.js +66 -79
- package/dist/web/public/assets/{activity-DYo7QS7k.js → activity-Cw0LRVNI.js} +1 -1
- package/dist/web/public/assets/activity-Cw0LRVNI.js.br +0 -0
- package/dist/web/public/assets/activity-Cw0LRVNI.js.gz +0 -0
- package/dist/web/public/assets/{boards-CwohTbtv.js → boards-DMVbV84c.js} +1 -1
- package/dist/web/public/assets/boards-DMVbV84c.js.br +0 -0
- package/dist/web/public/assets/boards-DMVbV84c.js.gz +0 -0
- package/dist/web/public/assets/{code-p-Tq1bMK.js → code-C86xKk2k.js} +1 -1
- package/dist/web/public/assets/{explorer-50no_DrD.js → explorer-DTvsy5Rs.js} +1 -1
- package/dist/web/public/assets/explorer-DTvsy5Rs.js.br +0 -0
- package/dist/web/public/assets/explorer-DTvsy5Rs.js.gz +0 -0
- package/dist/web/public/assets/{ghostty-web-Bvhh3ZFk.js → ghostty-web-CvSZragH.js} +1 -1
- package/dist/web/public/assets/ghostty-web-CvSZragH.js.br +0 -0
- package/dist/web/public/assets/ghostty-web-CvSZragH.js.gz +0 -0
- package/dist/web/public/assets/{index-WrsNstrb.js → index-DHxoYoeq.js} +2 -2
- package/dist/web/public/assets/index-DHxoYoeq.js.br +0 -0
- package/dist/web/public/assets/index-DHxoYoeq.js.gz +0 -0
- package/dist/web/public/assets/{runs-BAXZF-6r.js → runs-jY40JBP-.js} +1 -1
- package/dist/web/public/assets/runs-jY40JBP-.js.br +0 -0
- package/dist/web/public/assets/runs-jY40JBP-.js.gz +0 -0
- package/dist/web/public/assets/{settings-BmNXgRne.js → settings-Dux7PIKF.js} +1 -1
- package/dist/web/public/assets/settings-Dux7PIKF.js.br +0 -0
- package/dist/web/public/assets/settings-Dux7PIKF.js.gz +0 -0
- package/dist/web/public/assets/{task-runs-CpgHxFLf.js → task-runs-DKvtBVxS.js} +1 -1
- package/dist/web/public/assets/task-runs-DKvtBVxS.js.br +0 -0
- package/dist/web/public/assets/task-runs-DKvtBVxS.js.gz +0 -0
- package/dist/web/public/assets/{tasks-DybtogPF.js → tasks-CqfrOVIt.js} +1 -1
- package/dist/web/public/assets/tasks-CqfrOVIt.js.br +0 -0
- package/dist/web/public/assets/tasks-CqfrOVIt.js.gz +0 -0
- package/dist/web/public/assets/{terminal-m2_WYu5W.js → terminal-DL_0yEy7.js} +2 -2
- package/dist/web/public/assets/terminal-DL_0yEy7.js.br +0 -0
- package/dist/web/public/assets/terminal-DL_0yEy7.js.gz +0 -0
- package/dist/web/public/index.html +1 -1
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/package.json +1 -1
- package/dist/web/public/assets/activity-DYo7QS7k.js.br +0 -0
- package/dist/web/public/assets/activity-DYo7QS7k.js.gz +0 -0
- package/dist/web/public/assets/boards-CwohTbtv.js.br +0 -0
- package/dist/web/public/assets/boards-CwohTbtv.js.gz +0 -0
- package/dist/web/public/assets/explorer-50no_DrD.js.br +0 -0
- package/dist/web/public/assets/explorer-50no_DrD.js.gz +0 -0
- package/dist/web/public/assets/ghostty-web-Bvhh3ZFk.js.br +0 -0
- package/dist/web/public/assets/ghostty-web-Bvhh3ZFk.js.gz +0 -0
- package/dist/web/public/assets/index-WrsNstrb.js.br +0 -0
- package/dist/web/public/assets/index-WrsNstrb.js.gz +0 -0
- package/dist/web/public/assets/runs-BAXZF-6r.js.br +0 -0
- package/dist/web/public/assets/runs-BAXZF-6r.js.gz +0 -0
- package/dist/web/public/assets/settings-BmNXgRne.js.br +0 -0
- package/dist/web/public/assets/settings-BmNXgRne.js.gz +0 -0
- package/dist/web/public/assets/task-runs-CpgHxFLf.js.br +0 -0
- package/dist/web/public/assets/task-runs-CpgHxFLf.js.gz +0 -0
- package/dist/web/public/assets/tasks-DybtogPF.js.br +0 -0
- package/dist/web/public/assets/tasks-DybtogPF.js.gz +0 -0
- package/dist/web/public/assets/terminal-m2_WYu5W.js.br +0 -0
- package/dist/web/public/assets/terminal-m2_WYu5W.js.gz +0 -0
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
// Fetch one configuration document
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
import { lookup } from "node:dns/promises";
|
|
5
|
-
import { request } from "node:https";
|
|
6
|
-
import { BlockList, isIP } from "node:net";
|
|
7
|
-
import { checkServerIdentity } from "node:tls";
|
|
1
|
+
// Fetch one configuration document from the operator's source URL: HTTPS with
|
|
2
|
+
// no credentials, redirects followed while they stay HTTPS, JSON capped at
|
|
3
|
+
// 1 MiB so a hostile or broken source cannot exhaust memory.
|
|
8
4
|
export const CONFIG_SYNC_BYTES = 1024 * 1024;
|
|
9
|
-
const
|
|
10
|
-
const blockedV6 = new BlockList();
|
|
11
|
-
for (const [address, prefix] of [
|
|
12
|
-
["0.0.0.0", 8], ["10.0.0.0", 8], ["100.64.0.0", 10], ["127.0.0.0", 8],
|
|
13
|
-
["169.254.0.0", 16], ["172.16.0.0", 12], ["192.0.0.0", 24], ["192.168.0.0", 16],
|
|
14
|
-
["198.18.0.0", 15], ["224.0.0.0", 4], ["240.0.0.0", 4],
|
|
15
|
-
])
|
|
16
|
-
blockedV4.addSubnet(address, prefix, "ipv4");
|
|
17
|
-
for (const [address, prefix] of [
|
|
18
|
-
["::", 96], ["::ffff:0:0", 96], ["64:ff9b::", 96], ["64:ff9b:1::", 48],
|
|
19
|
-
["100::", 64], ["2001::", 32], ["2002::", 16], ["fc00::", 7], ["fe80::", 10], ["ff00::", 8],
|
|
20
|
-
])
|
|
21
|
-
blockedV6.addSubnet(address, prefix, "ipv6");
|
|
22
|
-
const globalV6 = new BlockList();
|
|
23
|
-
globalV6.addSubnet("2000::", 3, "ipv6");
|
|
5
|
+
const MAX_REDIRECTS = 5;
|
|
24
6
|
export function configSourceUrl(raw) {
|
|
25
7
|
let url;
|
|
26
8
|
try {
|
|
@@ -34,64 +16,69 @@ export function configSourceUrl(raw) {
|
|
|
34
16
|
}
|
|
35
17
|
return url;
|
|
36
18
|
}
|
|
37
|
-
export function publicConfigAddress(address) {
|
|
38
|
-
const family = isIP(address);
|
|
39
|
-
return family !== 0 && (family !== 6 || globalV6.check(address, "ipv6")) &&
|
|
40
|
-
!(family === 4 ? blockedV4 : blockedV6).check(address, family === 4 ? "ipv4" : "ipv6");
|
|
41
|
-
}
|
|
42
19
|
export async function downloadConfig(raw, etag, signal) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
hostname: address.address, family: address.family, port: url.port || 443,
|
|
64
|
-
servername: isIP(hostname) ? undefined : hostname,
|
|
65
|
-
checkServerIdentity: (_host, cert) => checkServerIdentity(hostname, cert),
|
|
66
|
-
path: `${url.pathname}${url.search}`, method: "GET", signal,
|
|
67
|
-
headers: { host: url.host, accept: "application/json", ...(etag ? { "if-none-match": etag } : {}) },
|
|
68
|
-
}, (res) => {
|
|
69
|
-
const status = res.statusCode;
|
|
70
|
-
if (status !== 200 && status !== 304) {
|
|
71
|
-
res.destroy();
|
|
72
|
-
reject(new Error(status === 404 || status === 410
|
|
73
|
-
? "Source link was revoked or does not exist"
|
|
74
|
-
: `Source returned HTTP ${String(status)}`));
|
|
75
|
-
return;
|
|
20
|
+
let url = configSourceUrl(raw);
|
|
21
|
+
for (let hop = 0;; hop++) {
|
|
22
|
+
signal.throwIfAborted();
|
|
23
|
+
let res;
|
|
24
|
+
try {
|
|
25
|
+
res = await fetch(url, {
|
|
26
|
+
method: "GET", redirect: "manual", signal,
|
|
27
|
+
headers: { accept: "application/json", ...(etag ? { "if-none-match": etag } : {}) },
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new Error(signal.aborted ? "Configuration download timed out or was cancelled" : "Could not connect to source");
|
|
32
|
+
}
|
|
33
|
+
const location = res.status >= 300 && res.status < 400 ? res.headers.get("location") : null;
|
|
34
|
+
if (location) {
|
|
35
|
+
await res.body?.cancel().catch(() => { });
|
|
36
|
+
if (hop >= MAX_REDIRECTS)
|
|
37
|
+
throw new Error("Source redirected too many times");
|
|
38
|
+
try {
|
|
39
|
+
url = configSourceUrl(new URL(location, url).href);
|
|
76
40
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
reject(new Error("Source did not return JSON"));
|
|
80
|
-
return;
|
|
41
|
+
catch {
|
|
42
|
+
throw new Error("Source redirected to a location that is not HTTPS");
|
|
81
43
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
res.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (res.status !== 200 && res.status !== 304) {
|
|
47
|
+
await res.body?.cancel().catch(() => { });
|
|
48
|
+
throw new Error(res.status === 404 || res.status === 410
|
|
49
|
+
? "Source link was revoked or does not exist"
|
|
50
|
+
: `Source returned HTTP ${String(res.status)}`);
|
|
51
|
+
}
|
|
52
|
+
const status = res.status === 304 ? 304 : 200;
|
|
53
|
+
if (status === 200 && !/^application\/json(?:\s*;|$)/i.test(res.headers.get("content-type") ?? "")) {
|
|
54
|
+
await res.body?.cancel().catch(() => { });
|
|
55
|
+
throw new Error("Source did not return JSON");
|
|
56
|
+
}
|
|
57
|
+
return { status, etag: res.headers.get("etag"), body: await read(res, signal) };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function read(res, signal) {
|
|
61
|
+
const reader = res.body?.getReader();
|
|
62
|
+
if (!reader)
|
|
63
|
+
return "";
|
|
64
|
+
const chunks = [];
|
|
65
|
+
let size = 0;
|
|
66
|
+
try {
|
|
67
|
+
for (;;) {
|
|
68
|
+
const { done, value } = await reader.read();
|
|
69
|
+
if (done)
|
|
70
|
+
break;
|
|
71
|
+
size += value.length;
|
|
72
|
+
if (size > CONFIG_SYNC_BYTES)
|
|
73
|
+
throw new Error("Configuration exceeds 1 MiB");
|
|
74
|
+
chunks.push(Buffer.from(value));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
await reader.cancel().catch(() => { });
|
|
79
|
+
if (err instanceof Error && err.message === "Configuration exceeds 1 MiB")
|
|
80
|
+
throw err;
|
|
81
|
+
throw new Error(signal.aborted ? "Configuration download timed out or was cancelled" : "Configuration download failed");
|
|
82
|
+
}
|
|
83
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
97
84
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{F as e,I as t,K as n,M as r,R as i,T as a,V as o,z as s}from"./index-
|
|
1
|
+
import{F as e,I as t,K as n,M as r,R as i,T as a,V as o,z as s}from"./index-DHxoYoeq.js";var c=(e,t={})=>{let n=document.createElementNS(`http://www.w3.org/2000/svg`,e);for(let[e,r]of Object.entries(t))n.setAttribute(e,r);return n},l=t=>t===null?`-`:e(Date.now()-t),u={light:{edge:`#a3a3a3`,callback:`#0891b2`,message:`#d97706`,scheduler:{fill:`#f5f3ff`,stroke:`#a78bfa`,text:`#5b21b6`},console:{fill:`#eff6ff`,stroke:`#93c5fd`,text:`#1d4ed8`},task:{fill:`#fffbeb`,stroke:`#fbbf24`,text:`#92400e`},process:{fill:`#f5f5f5`,stroke:`#a3a3a3`,text:`#525252`,dash:`4 3`},streaming:{fill:`#ffffff`,stroke:`#10b981`,text:`#262626`},idle:{fill:`#fafafa`,stroke:`#d4d4d4`,text:`#737373`}},dark:{edge:`#7a7a7a`,callback:`#3fc3dd`,message:`#e0a13a`,scheduler:{fill:`#2b2440`,stroke:`#7d63c9`,text:`#c9b8f5`},console:{fill:`#1f2a3d`,stroke:`#4a7fbf`,text:`#a8c8f0`},task:{fill:`#3a2f1c`,stroke:`#b8862a`,text:`#f0d49a`},process:{fill:`#262626`,stroke:`#5c5c5c`,text:`#a3a3a3`,dash:`4 3`},streaming:{fill:`#22302a`,stroke:`#10b981`,text:`#e5e5e5`},idle:{fill:`#212121`,stroke:`#4a4a4a`,text:`#9a9a9a`}}},d=()=>u[document.documentElement.dataset.theme===`dark`?`dark`:`light`];function f(e,u,f){let p=`sessions`,m=`active`,h={sessions:[],runs:[],messages:[]},g=``,_=s(async()=>{let r=m,i=await o(`/api/activity?scope=${r}`,`Failed to load activity`);if(!i.ok){g=``,e.replaceChildren(t(`p`,`p-4 text-[13px] text-red-600`,i.error));return}if(r!==m)return;let a=JSON.stringify(i.value);if(`${r}:${a}`===g)return;g=`${r}:${a}`;let s=i.value;h={...s,sessions:s.sessions.map(e=>({...e,title:n(e.title)}))},v()});function v(){let n=t(`header`,`flex h-10 flex-none items-center gap-2 border-b border-neutral-200 px-4 max-md:hidden`,t(`span`,`font-medium`,`Activity`)),r=t(`div`,`tabstrip`);r.append(a(`Sessions`,p===`sessions`,()=>{p=`sessions`,v()}),a(`Relationships`,p===`dependencies`,()=>{p=`dependencies`,v()}));let i=t(`div`,`flex flex-none gap-1 border-b border-neutral-200 px-4 py-2`),o=e=>()=>{m=e,v(),_()};i.append(a(`Current`,m===`active`,o(`active`)),a(`Last 24h`,m===`recent`,o(`recent`)));let s=t(`div`,`min-h-0 flex-1 overflow-auto`);p===`sessions`?y(s):b(s),e.replaceChildren(n,r,i,s)}function y(e){let n=document.createElement(`table`);n.className=`w-full table-fixed text-left text-[12.5px]`,n.innerHTML=`<thead class="bg-neutral-50 text-[10.5px] uppercase text-neutral-400"><tr>
|
|
2
2
|
<th class="w-[50%] px-4 py-2 font-semibold md:w-[34%]">Session</th>
|
|
3
3
|
<th class="hidden w-[38%] px-2 py-2 font-semibold md:table-cell">Project</th>
|
|
4
4
|
<th class="px-2 py-2 font-semibold md:w-[14%]">State</th>
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as e,G as t,I as n,L as r,M as i,N as a,O as o,V as s,W as c,h as l}from"./index-
|
|
1
|
+
import{B as e,G as t,I as n,L as r,M as i,N as a,O as o,V as s,W as c,h as l}from"./index-DHxoYoeq.js";var u=e=>e.public&&e.token?`/p/${e.slug}-${e.token}/`:`/boards/${e.slug}/`;function d(d,f){let p=[],m=``,h=n(`header`,`sticky top-0 z-30 flex h-10 items-center gap-3 border-b border-neutral-200 bg-white px-4`,n(`span`,`font-medium max-md:hidden`,`Boards`)),g=n(`div`,`px-4 py-5`);d.append(n(`div`,`min-h-0 flex-1 overflow-y-auto`,h,g));async function _(n,r){let i=await t(`/api/boards/${n.slug}`,{public:r},`PATCH`);if(i.ok)return n.token=(await i.json().catch(()=>({}))).token??``,x();m=await e(i,`Could not change ${n.slug}`),await S()}async function v(e){m=await c(`/api/boards/${e}`,`DELETE`,`Could not delete ${e}`)??``,await S()}function y(e){let t=n(`div`,`group flex flex-col gap-1 border-b border-neutral-200/70 px-1 py-2.5 last:border-b-0`),i=document.createElement(`a`);i.className=`truncate font-medium text-neutral-800 hover:text-indigo-700`,i.href=u(e),i.target=`_blank`,i.rel=`noreferrer`,i.textContent=e.title;let s=n(`div`,`flex items-center gap-2`,i,n(`span`,`flex-none font-mono text-[11.5px] text-neutral-400`,e.slug)),c=o(``,``,e.public,t=>{e.public=t,_(e,t)});c.className=`ml-auto flex flex-none cursor-pointer items-center gap-1.5 text-[11.5px] text-neutral-500`,c.append(n(`span`,``,`Public`));let d=l(`Delete`,`flex-none cursor-pointer text-[11.5px] text-neutral-400 opacity-0 transition-opacity hover:text-red-600 group-hover:opacity-100`);d.title=`Renames the folder on disk; nothing is erased`,d.onclick=()=>void v(e.slug);let p=a(`flex-none cursor-pointer text-[11.5px] text-neutral-400 opacity-0 transition-opacity hover:text-indigo-600 group-hover:opacity-100`,()=>`${location.origin}${u(e)}`);p.title=`Copy the board's link`,s.append(c,p,d),t.append(s);let m=n(`div`,`flex flex-wrap items-center gap-x-3 gap-y-1 text-[11.5px] text-neutral-500`);e.description&&m.append(n(`span`,`min-w-0 truncate`,e.description)),m.append(n(`span`,`flex-none text-neutral-400`,r(Date.parse(e.updatedAt))));for(let t of e.sessions){let e=l(t.slice(0,8),`flex-none cursor-pointer font-mono text-indigo-600 hover:underline`);e.title=`Open session ${t}`,e.onclick=()=>f(t),m.append(e)}if(e.public&&e.token){let t=n(`span`,`flex-none font-mono text-neutral-400`,u(e));t.title=`Public URL — anyone holding this link can read the board, no password`,m.append(t)}return t.append(m),t}let b=[{title:`Public`,hint:`Readable at /p/<slug>-<token>/ by anyone holding the link.`,wanted:!0},{title:`Private`,hint:`Reachable from the Console only.`,wanted:!1}];function x(){let e=n(`div`,`mx-auto flex max-w-3xl flex-col`);p.length===0&&e.append(n(`p`,`rounded-xl border border-dashed border-neutral-300 px-4 py-6 text-center text-[12.5px] text-neutral-500`,`No boards yet. Ask an agent to build one — it writes a folder under ~/.pier/boards.`));for(let{title:t,hint:r,wanted:i}of b){let a=p.filter(e=>e.public===i);if(a.length===0)continue;let o=n(`section`,`mb-6`);o.append(n(`h2`,`text-[13px] font-semibold text-neutral-700`,t)),o.append(n(`p`,`mb-1 text-[11.5px] leading-snug text-neutral-500`,r));let s=n(`div`,`rounded-xl border border-neutral-200 bg-white px-3`);s.append(...a.map(y)),o.append(s),e.append(o)}g.replaceChildren(...m?[n(`p`,`mb-3 text-[13px] text-red-600`,m)]:[],e)}async function S(){let e=await s(`/api/boards`,`Could not load boards`);if(!e.ok)return m=e.error,x();p=e.value,x(),m=``}let C=i(d,()=>void S());return window.addEventListener(`focus`,()=>{C.visible&&S()}),C}export{d as createBoardsView};
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{I as e,u as t}from"./index-
|
|
1
|
+
import{I as e,u as t}from"./index-DHxoYoeq.js";var n=2e4,r=e=>e.map((e,t)=>({nums:[t+1],text:e,tone:``}));function i(t,r){let i=t.length<=n?r:null,o=Math.max(2,...t.map(e=>e.nums.reduce((e,t)=>Math.max(e,String(t).length),0))),s=e(`div`,`py-2 font-mono text-[12px] leading-[1.5]`);for(let n of t){let t=e(`div`,`flex pl-2 pr-4 ${n.tone===`add`?`bg-emerald-50`:n.tone===`del`?`bg-red-50`:``}`);for(let r of n.nums){let n=e(`span`,`flex-none select-none pr-1.5 text-right text-neutral-300`,r===``?``:String(r));n.style.width=`calc(${o}ch + 0.375rem)`,t.append(n)}t.append(a(n,i)),s.append(t)}return s}function a(n,r){let i=`min-w-0 flex-1 whitespace-pre-wrap [overflow-wrap:anywhere]`;if(!n.mark||n.mark[0]>=n.mark[1]){let e=t(n.text,r);return e.className=i,e}let[a,o]=n.mark,s=t(n.text.slice(a,o),r);return s.className=`rounded-xs ${n.tone===`add`?`bg-emerald-200/80`:`bg-red-200/70`}`,e(`span`,i,t(n.text.slice(0,a),r),s,t(n.text.slice(o),r))}export{r as n,i as t};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{H as e,I as t,L as n,M as r,P as i,a,i as o,j as s,l as c,n as l,s as u}from"./index-
|
|
1
|
+
import{H as e,I as t,L as n,M as r,P as i,a,i as o,j as s,l as c,n as l,s as u}from"./index-DHxoYoeq.js";import{n as d,t as f}from"./code-C86xKk2k.js";var p=e=>{let t=new Date(e),n=e=>String(e).padStart(2,`0`);return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`},m=e=>[e.subject,e.body,[[e.author,e.email?`<${e.email}>`:``].filter(Boolean).join(` `),e.at?p(e.at):``].filter(Boolean).join(` · `)].filter(Boolean).join(`
|
|
2
2
|
|
|
3
3
|
`),h=null;function g(){h&&(h.remove(),h=null,document.removeEventListener(`pointerdown`,g,!0),window.removeEventListener(`scroll`,g,!0))}function _(e,n){e.onmouseenter=()=>{g();let r=n();if(!r)return;h=t(`div`,`pointer-events-none fixed z-50 max-w-96 whitespace-pre-wrap rounded-lg bg-neutral-800/95 px-3 py-2 font-sans text-[11.5px] leading-snug text-neutral-100 shadow-lg`),h.textContent=r,document.body.append(h),document.addEventListener(`pointerdown`,g,!0),window.addEventListener(`scroll`,g,!0);let i=e.getBoundingClientRect();h.style.left=`${Math.max(8,Math.min(i.left,window.innerWidth-h.offsetWidth-8))}px`,h.style.top=i.bottom+6+h.offsetHeight>window.innerHeight?`${i.top-h.offsetHeight-6}px`:`${i.bottom+6}px`},e.onmouseleave=g}function v(e,r,i){let a=t(`button`,`flex w-full cursor-pointer items-center gap-1.5 px-2 py-1 text-left hover:bg-neutral-100`,t(`span`,`w-3 flex-none text-indigo-600`,e.value===r?`✓`:``),t(`span`,`flex-none font-mono text-[11.5px] text-neutral-800`,e.label??e.value));return e.subject&&a.append(t(`span`,`min-w-0 truncate text-[11px] text-neutral-400`,e.subject)),e.author&&a.append(t(`span`,`ml-auto flex-none pl-1.5 text-[10.5px] text-neutral-400`,`${e.author} · ${e.at?n(e.at):``}`)),_(a,()=>m(e)),a.onclick=()=>i(e.value),a}function ee(e,n,r,i,s){let{base:c,head:l}=r,u=()=>l&&c===`${l}~1`?l:``,d=u()?`commit`:`compare`,f=n.commits[0],p=e=>[...e?[{value:``,label:`Working tree`,subject:`the files on disk, uncommitted changes included`}]:[],{...f,value:`HEAD`},...n.refs.map(e=>({value:e.name,subject:e.subject})),...n.commits.map(e=>({...e,value:e.hash}))],m=t(`div`,`flex w-[38rem] max-w-full flex-col`);function h(){let e=(e,n)=>{let r=t(`button`,`cursor-pointer rounded-full px-2.5 py-0.5 text-[11.5px] ${d===n?`bg-indigo-50 font-medium text-indigo-700`:`text-neutral-500 hover:bg-neutral-100`}`,e);return r.onclick=()=>{d=n,h()},r},r=(e,n,r,i)=>t(`div`,`flex min-w-0 flex-1 flex-col`,t(`div`,`flex-none px-2 pb-0.5 text-[10.5px] font-semibold uppercase tracking-wide text-neutral-400`,e),t(`div`,`min-h-0 flex-1 overflow-y-auto`,...n.map(e=>v(e,r,i)))),a=d===`compare`?t(`div`,`flex h-72 min-h-0 divide-x divide-neutral-200 pb-1`,r(`base`,p(!1),c,e=>{c=e,h(),i(c,l)}),r(`to`,p(!0),l,e=>{l=e,h(),i(c,l)})):t(`div`,`flex h-72 min-h-0 flex-col overflow-y-auto pb-1`,...n.commits.map(e=>v({value:e.hash,...e},u(),e=>{c=`${e}~1`,l=e,o(),i(c,l)}))),f=t(`div`,`flex-none border-t border-neutral-200`,v({value:s,label:`↺ ${s} ↔ Working tree`,subject:`back to the default: the main line's tip vs the files on disk`},c===s&&l===``?s:``,()=>{c=s,l=``,h(),i(c,l)}));m.replaceChildren(t(`div`,`flex flex-none items-center gap-1 border-b border-neutral-200 px-2 py-1.5`,e(`Compare two points`,`compare`),e(`One commit`,`commit`)),a,...d===`compare`?[f]:[])}h(),a(e,m)}var te=/\.(png|jpe?g|gif|webp|avif|bmp|svg)$/i,y={A:`text-emerald-600`,M:`text-amber-600`,D:`text-red-600`},ne=30,b=`pier.filesPrefs`,x=50,S=()=>{try{let e=JSON.parse(localStorage.getItem(b)??`{}`);return e&&typeof e==`object`?e:{}}catch{return{}}},re=e=>{let t=e?S()[e]:void 0;return typeof t?.cwd==`string`&&typeof t.base==`string`&&typeof t.head==`string`?{cwd:t.cwd,base:t.base,head:t.head}:null},ie=(e,t)=>{let n=S();delete n[e],n[e]=t;let r=Object.keys(n);for(let e of r.slice(0,Math.max(0,r.length-x)))delete n[e];try{localStorage.setItem(b,JSON.stringify(n))}catch(e){console.warn(`Files: could not remember this folder`,e)}},C=(e,n=`indigo`)=>{let r=t(`button`,`min-w-0 cursor-pointer truncate rounded-md px-2 py-0.5 text-left font-mono text-[11.5px] ${n===`indigo`?`bg-indigo-50 font-medium text-indigo-700 hover:bg-indigo-100`:`bg-neutral-100 text-neutral-600 hover:bg-neutral-200`}`,e);return r.type=`button`,r};function w(n,a,o,p,h){let g=``,v=``,b={branch:null,refs:[],commits:[],worktrees:[]},x=`HEAD`,S=``,w=new Map,T=!0,E=!1,D=[],O=new Set,k=null,A=null,ae=t(`header`,`flex h-10 flex-none items-center gap-2 border-b border-neutral-200 bg-white px-3`),j=t(`div`,`flex-none border-b border-neutral-200`),M=t(`div`,`min-h-0 flex-1 overflow-y-auto py-1`),oe=t(`aside`,`flex w-64 flex-none flex-col border-r border-neutral-200 text-[12.5px] max-md:h-2/5 max-md:w-full max-md:border-b max-md:border-r-0`,j,M),N=t(`div`,`h-full min-w-0 overflow-auto`),P=t(`div`,`pointer-events-none absolute inset-y-0 right-0 z-20 hidden w-2`),se=t(`section`,`relative min-h-0 min-w-0 flex-1`,N,P);n.append(ae,t(`div`,`flex min-h-0 flex-1 max-md:flex-col`,oe,se));let F=(e,t)=>`/api/explorer/${e}?${new URLSearchParams({root:g,...t})}`,I=(e,t)=>`/api/fs/${e}?${new URLSearchParams({root:g,...t})}`,L=(e,n=`text-neutral-400`)=>t(`p`,`px-4 py-3 text-[12.5px] ${n}`,e),R=e=>{let t=0;for(let n of w.keys())n.startsWith(`${e}/`)&&t++;return t};function z(e,t){let n=new Set(t.map(e=>e.name)),r=e?`${e}/`:``;for(let[e,i]of w){if(i.status!==`D`||!e.startsWith(r))continue;let a=e.slice(r.length).split(`/`)[0];n.has(a)||(n.add(a),t.push({name:a,dir:e.slice(r.length).includes(`/`)}))}let i=t;return T&&b.branch&&(i=t.filter(e=>e.dir?R(`${r}${e.name}`)>0:w.has(`${r}${e.name}`))),i.sort((e,t)=>Number(t.dir)-Number(e.dir)||e.name.localeCompare(t.name)),i.map(e=>e.dir?ce(`${r}${e.name}`,e.name):le(`${r}${e.name}`,e.name))}async function B(t,n){try{let{entries:r}=await e(I(`ls`,{path:n}),`could not list this folder`);t.replaceChildren(...z(n,r)),t.childElementCount||t.append(L(T?`No changes.`:`Empty.`))}catch(e){let r=z(n,[]);r.length?t.replaceChildren(...r):t.replaceChildren(L(String(e),`text-red-600`))}}function ce(e,n){let r=R(e),a=[t(`span`,`truncate`,n)];r&&a.push(t(`span`,`ml-auto flex-none rounded bg-amber-100/80 px-1 font-mono text-[10px] text-amber-700`,String(r)));let{el:o,summary:s}=i(``,a);s.className+=` px-2 py-0.5 hover:bg-neutral-100`;let c=t(`div`,`pl-3`);o.append(c);let l=!1,u=()=>{l||(l=!0,c.append(L(`…`)),B(c,e))};return o.ontoggle=()=>{O[o.open?`add`:`delete`](e),o.open&&u()},(T&&b.branch&&!E||O.has(e))&&(o.open=!0,u()),o}function V(e){A?.classList.remove(`bg-indigo-50`,`text-indigo-700`),A=e,e.classList.add(`bg-indigo-50`,`text-indigo-700`)}function le(e,n){let r=w.get(e)?.status,i=t(`button`,`flex w-full cursor-pointer items-center gap-1.5 px-2 py-0.5 pl-6 text-left hover:bg-neutral-100`,t(`span`,`truncate`,n));return r&&i.append(t(`span`,`ml-auto flex-none font-mono text-[10.5px] font-semibold ${y[r]??`text-neutral-500`}`,r)),i.title=e,e===k&&V(i),i.onclick=()=>{V(i),k=e,Y(e)},i}let H=()=>void B(M,``),U=(e,n)=>[...e?[t(`span`,`flex-none font-mono text-[10.5px] font-semibold text-emerald-600`,`+${e}`)]:[],...n?[t(`span`,`flex-none font-mono text-[10.5px] font-semibold text-red-600`,`−${n}`)]:[]];function W(e,n,r){let i=w.get(e),a=t(`div`,`sticky top-0 z-10 flex items-center gap-2 border-b border-neutral-200 bg-white px-4 py-1.5 font-mono text-[11.5px] text-neutral-500`,t(`span`,`truncate`,e));i&&(a.append(t(`span`,`flex-none font-semibold ${y[i.status]??``}`,i.status)),a.append(...U(i.add,i.del)));let o=t(`div`,`ml-auto flex flex-none items-center gap-2`);if(r&&o.append(r),n){let t=document.createElement(`a`);t.className=`flex-none text-neutral-400 hover:text-indigo-700 hover:underline`,t.href=I(`file`,{path:e}),t.download=s(e),t.textContent=`Download`,o.append(t)}return a.append(o),a}function ue(e){let t=0;for(;t<e.length;){if(e[t].tone!==`del`){t++;continue}let n=t;for(;t<e.length&&e[t].tone===`del`;)t++;let r=t;for(;t<e.length&&e[t].tone===`add`;)t++;let i=Math.min(r-n,t-r);for(let t=0;t<i;t++){let i=e[n+t],a=e[r+t],o=Math.min(i.text.length,a.text.length),s=0;for(;s<o&&i.text[s]===a.text[s];)s++;let c=0;for(;c<o-s&&i.text[i.text.length-1-c]===a.text[a.text.length-1-c];)c++;s+c!==0&&(i.mark=[s,i.text.length-c],a.mark=[s,a.text.length-c])}}}let G=_e();function K(){P.classList.add(`hidden`),P.replaceChildren(),G.set([],[])}let q=0,J=e=>e===q;function Y(e){let t=++q;return w.has(e)?ve(e,t):de(e,t)}async function de(e,n){if(!J(n))return;K();let r=I(`file`,{path:e}),i=t(`div`,`min-w-0`);if(N.classList.remove(`flex`,`flex-col`),N.replaceChildren(W(e,!0),i),te.test(e)){let e=t(`img`,`max-w-full p-4`);e.src=r,e.onerror=()=>i.replaceChildren(L(`Could not load image.`,`text-red-600`)),i.append(e);return}if(e.toLowerCase().endsWith(`.pdf`)){let t=document.createElement(`iframe`);t.className=`h-full w-full`,t.src=r,N.replaceChildren(W(e,!0),t),N.classList.add(`flex`,`flex-col`);return}i.append(L(`…`));let a=await fetch(r);if(!J(n))return;if(!a.ok){let{error:e}=await a.json().catch(()=>({}));i.replaceChildren(L(e??`HTTP ${a.status}`,`text-red-600`));return}if(!(a.headers.get(`content-type`)??``).startsWith(`text/plain`)){i.replaceChildren(L(`Binary file — use Download.`));return}let o=(await a.text()).split(`
|
|
4
4
|
`);if(!J(n))return;o.at(-1)===``&&o.pop();let s=await c(e);J(n)&&i.replaceChildren(f(d(o),s))}function fe(e){let t=[],n=0,r=0,i=!1,a=e.split(`
|
|
Binary file
|
|
Binary file
|