ccdeck 3.28.1 → 3.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -215,6 +215,8 @@ The deck installs that package itself, so it installs it **bounded**: `claude-sw
215
215
 
216
216
  **`↗`** in the panel header does the same for a set of them, which is what moving your accounts from home to work actually is. Tick the ones to send — all of them to start — and one blob carries the set. The dialog counts sign-in tokens rather than rows, and an account that cannot be exported is named rather than quietly dropped, so the number on the copy button is always the number in the blob.
217
217
 
218
+ **On macOS, the login itself lives in the Keychain.** If ccdeck is running from SSH, a LaunchAgent, or another session that cannot open that Keychain, the account may still be valid while this process cannot read or share it. ccdeck shows that state as **cannot share here** instead of calling the login expired, refuses clipboard and LAN exports from it, and tells you to start ccdeck from a Terminal window on the Mac itself. Once claude-swap can read the Keychain again, the account becomes shareable normally.
219
+
218
220
  An import adds what is missing and leaves a working account exactly as it is. The one it does rewrite unasked is a slot claude-swap has itself quarantined as refresh-token-dead, which is what heals a machine whose login stopped working. The result names every account in the paste — imported, already here, healed, or refused — and an account it skipped can be overwritten one at a time with **update anyway**.
219
221
 
220
222
  > [!WARNING]
@@ -253,6 +255,8 @@ Four pictures, which are also the guide the section opens from `See how it works
253
255
 
254
256
  **What is shared, and with whom.** Nothing until you tick a login, and then only that login, and only with decks somebody at this machine accepted — the deck asks the machines it finds, and a machine that is asked waits for somebody there to press `accept` — unless its `Say yes to every deck that asks` switch, in the same dialog, is on. A paired deck can fill an *expired* slot of this deck's and nothing else: it cannot overwrite a login that still works here. What crosses the wire is the same live credential a share carries, sealed to the deck it is addressed to, so treat the pairing decision as the moment that matters. Unpairing stops future rounds; a login already copied stays where it went.
255
257
 
258
+ On a Mac, **cannot share here** is different from **expired here**. It means this ccdeck process cannot read the Keychain copy, so that machine is not used as a credential source and another deck does not keep trying to “repair” the same healthy slot every minute. A LAN import is checked after it lands too; if the receiving Mac still cannot read the Keychain, the round reports that problem instead of claiming the login was repaired.
259
+
256
260
  ## Options
257
261
 
258
262
  ```
@@ -61,7 +61,7 @@ arrives with its own licence beside it. They are named for completeness, so that
61
61
  "what ccdeck ships" and "what ccdeck can run" are not confused. Every one of them
62
62
  is off under `AGENTS_DECK_NO_INSTALL=1`.
63
63
 
64
- ## Bundled packages (25)
64
+ ## Bundled packages (26)
65
65
 
66
66
  | Package | Version | Licence | Copyright |
67
67
  | --- | --- | --- | --- |
@@ -82,6 +82,7 @@ is off under `AGENTS_DECK_NO_INSTALL=1`.
82
82
  | `d3-zoom` | 3.0.0 | ISC | Copyright 2010-2021 Mike Bostock |
83
83
  | `dagre` | 0.8.5 | MIT | Copyright (c) 2012-2014 Chris Pettitt |
84
84
  | `graphlib` | 2.1.8 | MIT | Copyright (c) 2012-2014 Chris Pettitt |
85
+ | `hls.js` | 1.7.3 | Apache-2.0 | Copyright (c) 2017 Dailymotion (http://www.dailymotion.com) |
85
86
  | `lodash` | 4.18.1 | MIT | Copyright OpenJS Foundation and other contributors <https://openjsf.org/> |
86
87
  | `react` | 18.3.1 | MIT | Copyright (c) Facebook, Inc. and its affiliates. |
87
88
  | `react-dom` | 18.3.1 | MIT | Copyright (c) Facebook, Inc. and its affiliates. |
@@ -91,10 +92,18 @@ is off under `AGENTS_DECK_NO_INSTALL=1`.
91
92
  | `vite` | 6.4.3 | MIT | Copyright (c) 2019-present, VoidZero Inc. and Vite contributors |
92
93
  | `zustand` | 4.5.7 | MIT | Copyright (c) 2019 Paul Henschel |
93
94
 
94
- Every one is a permissive licence (MIT, ISC, BSD-3-Clause). All are
95
- compatible with the GNU AGPL v3: permissively licensed code may be combined
95
+ Every one is a permissive licence (MIT, ISC, BSD-3-Clause, Apache-2.0). All
96
+ are compatible with the GNU AGPL v3: permissively licensed code may be combined
96
97
  into an AGPL-licensed work provided its copyright and permission notices are
97
- preserved, which is the purpose of this file. None of these licences is
98
+ preserved, which is the purpose of this file. Apache-2.0 is the one that asks
99
+ for more than a notice — a copy of the licence itself travels with the work —
100
+ and it is compatible with version 3 of the GNU licences, which is the version
101
+ ccdeck is under.
102
+
103
+ `hls.js` is the one entry that is not in the page every visit loads. It is its
104
+ own chunk, `dist/web/assets/hls-*.js`, fetched only when somebody plays an HLS
105
+ custom station in a browser without native HLS (#1208) — but that chunk is in
106
+ `dist/web` like every other, and so in the tarball. None of these licences is
98
107
  changed by ccdeck's relicensing, and no copyleft obligation flows back to
99
108
  their authors.
100
109
 
@@ -501,3 +510,138 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
501
510
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
502
511
  THE SOFTWARE.
503
512
  ```
513
+
514
+ ### 14. `hls.js` 1.7.3
515
+
516
+ Reproduced verbatim from `node_modules/hls.js/LICENSE`:
517
+
518
+ ```text
519
+ Copyright (c) 2017 Dailymotion (http://www.dailymotion.com)
520
+
521
+ Licensed under the Apache License, Version 2.0 (the "License");
522
+ you may not use this file except in compliance with the License.
523
+ You may obtain a copy of the License at
524
+
525
+ http://www.apache.org/licenses/LICENSE-2.0
526
+
527
+ Unless required by applicable law or agreed to in writing, software
528
+ distributed under the License is distributed on an "AS IS" BASIS,
529
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
530
+ See the License for the specific language governing permissions and
531
+ limitations under the License.
532
+
533
+ src/remux/mp4-generator.js and src/demux/exp-golomb.ts implementation in this project
534
+ are derived from the HLS library for video.js (https://github.com/videojs/videojs-contrib-hls)
535
+
536
+ That work is also covered by the Apache 2 License, following copyright:
537
+ Copyright (c) 2013-2015 Brightcove
538
+
539
+
540
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
541
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
542
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
543
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
544
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
545
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
546
+ THE SOFTWARE.
547
+ ```
548
+
549
+ `hls.js` builds four smaller works into its own distribution, and they reach
550
+ `dist/web/assets/hls-*.js` with it. Their notices, as the `hls.js` build carries
551
+ them:
552
+
553
+ - **eventemitter3** — MIT, Copyright (c) 2014 Arnout Kazemier. The MIT
554
+ permission notice and disclaimer are those in §1 above.
555
+ - **vtt.js** — Apache-2.0, `Copyright 2013 vtt.js Contributors`. Licensed under
556
+ the Apache License, Version 2.0, below.
557
+ - **DASH Industry Forum** code (the CEA-608 caption parser) — BSD-3-Clause:
558
+
559
+ ```text
560
+ Copyright (c) 2015-2016, DASH Industry Forum.
561
+ All rights reserved.
562
+
563
+ Redistribution and use in source and binary forms, with or without modification,
564
+ are permitted provided that the following conditions are met:
565
+ 1. Redistributions of source code must retain the above copyright notice, this
566
+ list of conditions and the following disclaimer.
567
+ * Redistributions in binary form must reproduce the above copyright notice,
568
+ this list of conditions and the following disclaimer in the documentation and/or
569
+ other materials provided with the distribution.
570
+ 2. Neither the name of Dash Industry Forum nor the names of its
571
+ contributors may be used to endorse or promote products derived from this software
572
+ without specific prior written permission.
573
+
574
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
575
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
576
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
577
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
578
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
579
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
580
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
581
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
582
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
583
+ POSSIBILITY OF SUCH DAMAGE.
584
+ ```
585
+
586
+ - **utf.js** — `Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>`: "This library
587
+ is free. You can redistribute it and/or modify it."
588
+
589
+ The Apache License, Version 2.0, which `hls.js` and vtt.js are under:
590
+
591
+ ```text
592
+ Apache License
593
+
594
+ Version 2.0, January 2004
595
+
596
+ http://www.apache.org/licenses/
597
+
598
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
599
+
600
+ 1. Definitions.
601
+
602
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
603
+
604
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
605
+
606
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
607
+
608
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
609
+
610
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
611
+
612
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
613
+
614
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
615
+
616
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
617
+
618
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
619
+
620
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
621
+
622
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
623
+
624
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
625
+
626
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
627
+
628
+ You must give any other recipients of the Work or Derivative Works a copy of this License; and
629
+
630
+ You must cause any modified files to carry prominent notices stating that You changed the files; and
631
+
632
+ You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
633
+
634
+ If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
635
+
636
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
637
+
638
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
639
+
640
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
641
+
642
+ 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
643
+
644
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
645
+
646
+ END OF TERMS AND CONDITIONS
647
+ ```
@@ -1 +1 @@
1
- import{r as l,j as e,u as se,h as te,s as ne,i as ae}from"./index-CsHTYwoD.js";import{S as ve,k as ye}from"./index-CsHTYwoD.js";function ie(n){const i=/^#?([0-9a-f]{6})$/i.exec((n??"").trim());if(!i)return!1;const o=parseInt(i[1],16),r=[o>>16&255,o>>8&255,o&255].map(b=>{const t=b/255;return t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4)});return .2126*r[0]+.7152*r[1]+.0722*r[2]>.5}const V=1e4,re=12e3;function oe({browsers:n,findings:i,watching:o,palette:r}){const b=l.useRef(null),t=l.useRef(null);return t.current={browsers:n,findings:i,watching:o},l.useEffect(()=>{var L;const w=b.current;if(!w)return;const a=w.getContext("2d");if(!a)return;const v=((L=window.matchMedia)==null?void 0:L.call(window,"(prefers-reduced-motion: reduce)").matches)??!1;let y=0,d=!1;const N=()=>{var H;if(d)return;const W=t.current;if(!W)return;const{browsers:D,findings:U,watching:O}=W,B=r["--accent"]||"#7dd3fc",G=r["--line"]||"#1f2229",_=r["--text-dim"]||"#6e727c",P=ie(r["--bg"]),F=P?42:26,J=P?.85:.65,A=Date.now(),k=Math.min(window.devicePixelRatio||1,2),M=w.clientWidth,R=w.clientHeight;(w.width!==M*k||w.height!==R*k)&&(w.width=M*k,w.height=R*k),a.setTransform(k,0,0,k,0,0),a.clearRect(0,0,M,R);const m=M/2,g=R/2,p=Math.min(M,R)/2-6;a.strokeStyle=G,a.lineWidth=1,a.globalAlpha=P?1:.9;for(const h of[.4,.7,1])a.beginPath(),a.arc(m,g,p*h,0,Math.PI*2),a.stroke();a.beginPath(),a.moveTo(m-p,g),a.lineTo(m+p,g),a.moveTo(m,g-p),a.lineTo(m,g+p),a.stroke(),a.globalAlpha=1;const j=(O&&!v?A%V/V:0)*Math.PI*2-Math.PI/2;if(O){const h=(H=a.createConicGradient)==null?void 0:H.call(a,j-.85,m,g);h&&(h.addColorStop(0,"transparent"),h.addColorStop(.85/(Math.PI*2),`color-mix(in srgb, ${B} ${F}%, transparent)`),h.addColorStop(1,"transparent"),a.fillStyle=h,a.beginPath(),a.moveTo(m,g),a.arc(m,g,p,j-.85,j),a.closePath(),a.fill()),a.strokeStyle=B,a.globalAlpha=J,a.beginPath(),a.moveTo(m,g),a.lineTo(m+Math.cos(j)*p,g+Math.sin(j)*p),a.stroke(),a.globalAlpha=1}D.forEach((h,S)=>{const $=S/Math.max(D.length,1)*Math.PI*2-Math.PI/2,T=h.lastReadMs===null?1:Math.min(1,(A-h.lastReadMs)/12e4),E=p*(.42+T*.5),C=m+Math.cos($)*E,I=g+Math.sin($)*E,q=Math.abs((j-$+Math.PI*3)%(Math.PI*2)-Math.PI),K=O?Math.max(0,1-q/.55):0;a.beginPath(),a.arc(C,I,3+K*1.6,0,Math.PI*2),a.fillStyle=h.running?B:_,a.globalAlpha=h.running?.55+K*.45:.35,a.fill(),a.globalAlpha=1});for(const h of U){const S=(A-h.atMs)/re;if(S<0||S>1)continue;const T=Math.max(0,D.findIndex(I=>I.key===h.browser))/Math.max(D.length,1)*Math.PI*2-Math.PI/2,E=m+Math.cos(T)*p*.6,C=g+Math.sin(T)*p*.6;a.beginPath(),a.arc(E,C,4+S*22,0,Math.PI*2),a.strokeStyle=B,a.globalAlpha=(1-S)*.8,a.lineWidth=1.5,a.stroke(),a.globalAlpha=1}y=requestAnimationFrame(N)};return N(),()=>{d=!0,cancelAnimationFrame(y)}},[r]),e.jsx("canvas",{ref:b,className:"bw-radar",role:"img","aria-label":o?`Watching ${n.length} browser${n.length===1?"":"s"}`:"Not watching"})}function le(n,i,o){if(n===null)return null;const r=n+i-o;return r>0?r:null}function ce(n){const i=Math.ceil(n/1e3),o=Math.floor(i/60),r=i%60;return o>0?`${o}m ${r}s`:`${r}s`}function Z(n){return(n??[]).filter(i=>i.installed&&i.profiles>0)}function z(n,i){return i?{kind:"saving",word:"Saving",detail:""}:n.settings.enabled?{kind:"on",word:"Watching",detail:""}:{kind:"off",word:"Paused",detail:"nothing new is recorded"}}function de(n){return!Number.isFinite(n)||n<=0?"empty":n<1024?`${Math.round(n)} B`:n<1024*1024?`${Math.max(1,Math.round(n/1024))} KB`:`${(n/1024/1024).toFixed(1)} MB`}function he(n,i){return n.map(o=>({key:o.key,name:o.name,running:o.running,visits:i.filter(r=>r.browser===o.key).reduce((r,b)=>r+b.programVisits,0)}))}function ue(n,i){if(n===null)return null;const o=Math.max(0,Math.round((i-n)/1e3));if(o<5)return"just now";if(o<60)return`${o} sec ago`;const r=Math.round(o/60);return r<60?`${r} min ago`:`${Math.round(r/60)} hr ago`}function we(n){const i=n.profiles.filter(r=>r.degraded);if(i.length>0){const r=i[0],b=i.length-1;return{kind:"unreadable",text:`${r.name}/${r.profile} could not be read — ${r.reason??"no reason given"}`+(b>0?`, and ${b} more`:"")+". The watch continues on the profiles it can read."}}const o=Z(n.browsers);return o.length===0?{kind:"no-profiles",text:"No browser on this machine has a profile to read. Open one of the browsers below once and it will be watched from then on."}:o.some(r=>r.running)?null:{kind:"none-running",text:"No watched browser is running, so nothing can be navigated right now. Their history is still read, and anything found while they were open is still here."}}function me(n){const i=o=>new Date(o).toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1});return n.count===1||n.endMs-n.startMs<6e4?i(n.startMs):`${i(n.startMs)} → ${i(n.endMs)}`}function pe(n){return new Date(n).toLocaleDateString(void 0,{weekday:"short",day:"numeric",month:"short"})}function X(n){const i=Math.round((n.endMs-n.startMs)/6e4);return i<1?"":`${i} min`}function fe({onClose:n,onSeen:i,onWatching:o,palette:r}){const b=se(n),[t,w]=l.useState(null),[a,v]=l.useState(null),[y,d]=l.useState(null),[N,L]=l.useState(!1),W=l.useRef(!1),[D,U]=l.useState(null),[O,B]=l.useState(null),[G,_]=l.useState(!1),P=l.useRef(!1),[F,J]=l.useState(!1),[A,k]=l.useState(!1),[M,R]=l.useState(!1),[m,g]=l.useState(!1),[p,Y]=l.useState(!1),[j,H]=l.useState(()=>Date.now());l.useEffect(()=>{const s=setInterval(()=>H(Date.now()),1e3);return()=>clearInterval(s)},[]);const h=l.useCallback(async s=>{var c,x;if(!(s&&!te(W.current))){W.current=s,L(!0);try{const u=await fetch(`/api/browser-watch${s?"?refresh=1":""}`);if(!u.ok)throw new Error(`the deck answered ${u.status}`);const f=await u.json();w(f),P.current||U(((c=f==null?void 0:f.settings)==null?void 0:c.quietMinutes)??15),o(((x=f==null?void 0:f.settings)==null?void 0:x.enabled)===!0),v(null)}catch(u){v(u instanceof Error?u.message:String(u))}finally{W.current=!1,L(!1)}}},[o]);l.useEffect(()=>{h(!1)},[h]),l.useEffect(()=>{const s=setInterval(()=>{h(!1)},1e4);return()=>clearInterval(s)},[h]);const S=l.useCallback(async s=>{try{await fetch("/api/browser-watch/dismiss",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({host:s.host,startMs:s.startMs})})}finally{await h(!1)}},[h]),$=l.useCallback(async s=>{P.current=!0,_(!0);try{const c=await fetch("/api/browser-watch",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!c.ok)throw new Error(`the deck answered ${c.status}`);await h(!1),d(null)}catch(c){d(c instanceof Error?c.message:String(c))}finally{P.current=!1,_(!1)}},[h]),T=l.useRef(i);T.current=i,l.useEffect(()=>()=>T.current(Date.now()),[]);const E=l.useMemo(()=>{const s=[];for(const c of(t==null?void 0:t.episodes)??[]){const x=pe(c.startMs),u=s[s.length-1];u&&u.label===x?u.episodes.push(c):s.push({label:x,episodes:[c]})}return s},[t]),C=Z(t==null?void 0:t.browsers),I=((t==null?void 0:t.browsers)??[]).filter(s=>!(s.installed&&s.profiles>0)),q=t?we(t):null,K=C.reduce((s,c)=>s+c.profiles,0),Q=t?le(t.coverage.lastHumanMs,t.coverage.quietMs,j):null;return e.jsx("div",{className:"modal-backdrop",onClick:n,role:"presentation",children:e.jsxs("div",{ref:b,className:"modal bw-modal",onClick:s=>s.stopPropagation(),role:"dialog","aria-modal":"true","aria-labelledby":"bw-title",children:[e.jsxs("header",{className:"modal-head",children:[e.jsxs("div",{className:"modal-title",children:[e.jsx("span",{className:"modal-tool-name",id:"bw-title",children:"Browser watch"}),e.jsx("span",{className:"modal-tool-id",children:t?`${K} ${K===1?"profile":"profiles"} · local only`:"local only"})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"glyph-btn",onClick:()=>void h(!0),...ne(N),"aria-label":"Re-read every profile now",title:"Re-read every profile now",children:N?"…":"↻"}),e.jsx("button",{className:"glyph-btn",onClick:n,"aria-label":"Close (Esc)",title:"Close (Esc)",children:"×"})]})]}),e.jsxs("div",{className:"modal-body bw-body",children:[!t&&!a&&e.jsxs("div",{className:"bw-loading",children:[e.jsx("span",{className:"bw-loading-bar","aria-hidden":!0}),e.jsx("p",{children:"Reading each browser's history. The first look copies the file, which takes a moment."})]}),a&&e.jsx("div",{className:"bw-state",children:e.jsxs("div",{className:"bw-row err",children:[e.jsx("span",{className:"bw-dot","aria-hidden":!0}),e.jsx("span",{className:"bw-row-label",children:"Could not read"}),e.jsx("span",{className:"bw-row-detail",children:a})]})}),t&&q&&e.jsx("p",{className:`bw-trouble ${q.kind}`,role:"status",children:q.text}),t&&e.jsxs("div",{className:"bw-work",children:[e.jsxs("aside",{className:"bw-side",children:[e.jsxs("section",{className:"bw-sec",children:[e.jsxs("h4",{className:"bw-sec-head",children:["Activity overview",e.jsx("button",{className:"bw-help",onClick:()=>R(s=>!s),"aria-expanded":M,"aria-label":"What the sweep and the dots mean",children:"?"})]}),e.jsxs("div",{className:"bw-glance",children:[e.jsx(oe,{browsers:C.map(s=>{var c;return{key:s.key,name:s.name,running:s.running,lastReadMs:((c=t.profiles.find(x=>x.browser===s.key))==null?void 0:c.lastWrittenMs)??null}}),findings:t.episodes.slice(0,6).map(s=>({browser:s.browser??null,atMs:s.endMs})),watching:t.settings.enabled,palette:r}),e.jsx("ul",{className:"bw-counts",children:he(C,t.profiles).map(s=>e.jsxs("li",{children:[e.jsx("span",{className:"bw-count-n",children:s.visits.toLocaleString("en-US")}),e.jsx("span",{className:"bw-count-of",children:s.name})]},s.key))})]}),e.jsx("p",{className:"bw-since",children:"Pages a program opened, since this deck started"}),t.coverage.checkedMs>0&&e.jsxs("p",{className:"bw-since",children:["Last checked ",ue(t.coverage.checkedMs,j),t.coverage.checks>0&&e.jsxs(e.Fragment,{children:[" · ",t.coverage.checks.toLocaleString("en-US")," ",t.coverage.checks===1?"check":"checks"]})]}),Q!==null&&e.jsxs("p",{className:"bw-gate",children:["You are browsing — a program page would count in ",ce(Q)]}),M&&e.jsxs("dl",{className:"bw-key",children:[e.jsx("dt",{children:"Sweep"}),e.jsx("dd",{children:"one poll of every watched profile"}),e.jsx("dt",{children:"Dot"}),e.jsx("dd",{children:"a browser, further out the longer since it last wrote history"}),e.jsx("dt",{children:"Ring"}),e.jsx("dd",{children:"a finding, leaving the browser it came from"})]})]}),e.jsxs("section",{className:"bw-sec",children:[e.jsxs("h4",{className:"bw-sec-head",children:["Watched profiles",e.jsx("button",{className:"bw-help",onClick:()=>g(s=>!s),"aria-expanded":m,"aria-label":"What running, idle and connected to Anthropic mean",children:"?"})]}),m&&e.jsxs("dl",{className:"bw-key",children:[e.jsx("dt",{children:"Running"}),e.jsx("dd",{children:"the browser has a process on this machine right now"}),e.jsx("dt",{children:"Connected"}),e.jsx("dd",{children:"an open connection to an address Anthropic's relay uses — which it shares with claude.ai, so an open tab looks the same as an agent channel"})]}),e.jsx("ul",{className:"bw-profiles",children:C.map(s=>e.jsxs("li",{children:[e.jsx("span",{className:`bw-prof-dot${s.running?" on":""}`,"aria-hidden":!0}),e.jsx("span",{className:"bw-prof-name",children:s.name}),e.jsx("span",{className:"bw-prof-state",children:s.running?"running":"idle"}),s.running&&s.relay.state==="live"&&e.jsx("span",{className:"bw-relay",children:"connected to Anthropic"})]},s.key))}),I.length>0&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"bw-why bw-rest-head",onClick:()=>Y(s=>!s),"aria-expanded":p,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:p?"▾":"▸"})," ",I.length," not watched"]}),p&&e.jsxs("p",{className:"bw-rest",children:[I.map(s=>s.name).join(", ")," — not installed, or installed and never opened, so there is no history to read."]})]})]}),t.relay&&e.jsx(be,{relay:t.relay})]}),e.jsxs("div",{className:"bw-main",children:[e.jsxs("section",{className:"bw-findings",children:[e.jsxs("h4",{className:"bw-sec-head bw-head-row",children:["Findings",t.episodes.length>0&&e.jsxs("span",{className:"bw-feed-count",children:[t.episodes.length," ",t.episodes.length===1?"episode":"episodes"]})]}),e.jsx("div",{className:"bw-eps",children:E.length===0?e.jsxs("div",{className:"bw-empty",children:[e.jsx("p",{className:"bw-empty-head",children:"Nothing found yet"}),e.jsxs("p",{className:"bw-empty-note",children:["An episode lands here when a program opens pages in a browser nobody has touched for"," ",t.settings.quietMinutes," ",t.settings.quietMinutes===1?"minute":"minutes",". On most machines that is rare, so an empty list is the ordinary result rather than a sign something is wrong."]}),!t.settings.enabled&&e.jsx("p",{className:"bw-empty-note",children:"Watching is off, so this shows only what this deck has seen since it started. Anything an earlier run recorded comes back when you switch it on."})]}):E.map(s=>e.jsxs("div",{className:"bw-day",children:[e.jsx("h4",{className:"bw-sec-head",children:s.label}),s.episodes.map(c=>{const x=`${c.host}-${c.startMs}`,u=O===x;return e.jsxs("div",{className:`bw-ep${u?" open":""}`,children:[e.jsxs("div",{className:"bw-ep-row",children:[e.jsxs("button",{className:"bw-ep-head",onClick:()=>B(u?null:x),"aria-expanded":u,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:u?"▾":"▸"}),e.jsx("span",{className:"bw-ep-host",children:c.host}),e.jsxs("span",{className:"bw-ep-meta",children:[me(c),X(c)&&e.jsxs(e.Fragment,{children:[" · ",X(c)]})]}),e.jsxs("span",{className:"bw-ep-count",children:[c.count," ",c.count===1?"page":"pages"]})]}),e.jsx("button",{className:"glyph-btn bw-ep-x",onClick:()=>void S(c),"aria-label":`Dismiss ${c.host}`,title:"Dismiss — it leaves this list for good, and stays in the log file",children:"×"})]}),u&&e.jsx("ul",{className:"bw-urls",children:c.urls.map((f,ee)=>e.jsxs("li",{children:[e.jsx("span",{className:"bw-url-time",children:new Date(f.timeMs).toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1})}),e.jsx("span",{className:"bw-url",children:f.url})]},`${f.url}-${f.timeMs}-${ee}`))})]},x)})]},s.label))})]}),e.jsxs("section",{className:"bw-feed",children:[e.jsxs("h4",{className:"bw-sec-head bw-head-row",children:["Live activity",e.jsxs("span",{className:"bw-feed-count",children:[t.log.length," ",t.log.length===1?"event":"events"]})]}),e.jsx("p",{className:"bw-since bw-feed-note",children:"Pages you opened yourself — counted to run the rule, never written down. Addresses appear under Findings, and only for pages a program opened."}),e.jsx("div",{className:"bw-log","aria-live":"polite","aria-relevant":"additions",children:t.log.length===0?e.jsx("p",{className:"bw-note",children:"The deck writes a line here each time it looks at a profile."}):t.log.map(s=>e.jsxs("div",{className:`bw-log-line ${s.level}${s.parts?"":" sys"}`,children:[e.jsx("span",{className:"bw-log-time",children:new Date(s.atMs).toLocaleTimeString("en-GB",{hour12:!1})}),s.parts?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"bw-log-browser",children:s.parts.browser}),e.jsx("span",{className:"bw-log-profile",title:s.parts.profile,children:s.parts.profile}),e.jsxs("span",{className:"bw-log-value",children:[s.parts.value,s.parts.flagged>0&&e.jsxs("span",{className:"bw-log-flag",children:[" · ",s.parts.flagged," flagged"]})]})]}):e.jsx("span",{className:"bw-log-sys",children:s.text})]},`${s.atMs}-${s.level}-${s.text}`))})]})]})]}),t&&F&&e.jsxs("div",{className:"bw-settings",children:[e.jsxs("label",{title:"A program opening a page counts as a finding only if nobody had touched the browser for this long. Shorter catches more and reports more of your own work; longer is quieter.",children:[e.jsx("span",{children:"Nobody browsing for"}),e.jsxs("select",{value:D??t.settings.quietMinutes,onChange:s=>{U(Number(s.target.value)),$({quietMinutes:Number(s.target.value)})},children:[e.jsx("option",{value:1,children:"1 min"}),e.jsx("option",{value:5,children:"5 min"}),e.jsx("option",{value:15,children:"15 min"}),e.jsx("option",{value:30,children:"30 min"}),e.jsx("option",{value:60,children:"60 min"})]})]}),e.jsxs("label",{children:[e.jsx("span",{children:"When it finds one"}),e.jsx("select",{value:t.settings.reaction,onChange:s=>void $({reaction:s.target.value}),disabled:!t.settings.enabled,title:t.settings.enabled?"What to do besides writing it down.":"Turn watching on to arm a reaction.",children:(t.reactions??["notify"]).map(s=>e.jsx("option",{value:s,children:s==="notify"?"notify me":s==="close-tab"?"close the tab":"quit the browser"},s))})]}),e.jsxs("div",{className:"bw-settings-note",children:[e.jsxs("p",{children:["Chrome marks a navigation that came from an extension or a command rather than from a click. These are the ones that happened while nobody had touched the browser for the time above. ",e.jsx("strong",{children:"Usually that is your own agent doing what you asked."})]}),e.jsxs("button",{className:"bw-why",onClick:()=>k(s=>!s),"aria-expanded":A,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:A?"▾":"▸"})," What Browser Watch can access"]}),A&&e.jsxs("dl",{className:"bw-access",children:[e.jsx("dt",{children:"Reads"}),e.jsxs("dd",{children:["A copy of each browser's own history database — the live file is locked while the browser holds it. Only rows newer than the moment this deck started:"," ",new Date(t.coverage.startedMs).toLocaleString(),"."]}),e.jsx("dt",{children:"Keeps"}),e.jsxs("dd",{children:["Only while the switch is on, and only the episodes it flagged — never your ordinary browsing. In ",e.jsx("code",{className:"bw-path",children:t.coverage.logPath}),typeof t.coverage.logBytes=="number"?` (${de(t.coverage.logBytes)})`:"",", with every address written in full so you can check it yourself."]}),e.jsx("dt",{children:"Sends"}),e.jsx("dd",{children:"Nothing. No part of this reads or writes over the network; the deck serves on 127.0.0.1 and this panel talks only to it."}),e.jsx("dt",{children:"Never reads"}),e.jsx("dd",{children:"Anything from before this deck started, cookies, saved passwords, page contents, or any browser profile with no history file."})]})]})]}),y&&e.jsx("div",{className:"bw-state bw-write-err",role:"alert",children:e.jsxs("div",{className:"bw-row err",children:[e.jsx("span",{className:"bw-dot","aria-hidden":!0}),e.jsx("span",{className:"bw-row-label",children:"Could not save that setting"}),e.jsxs("span",{className:"bw-row-detail",children:[y," — the switches below show what is stored, not what you pressed."]}),e.jsx("button",{className:"glyph-btn bw-write-err-x",onClick:()=>d(null),"aria-label":"Dismiss",title:"Dismiss",children:"×"})]})}),t&&e.jsxs("footer",{className:"bw-status",children:[e.jsx("span",{className:`bw-mode-dot${t.settings.enabled?" on":""}`,"aria-hidden":!0}),e.jsx("span",{className:"bw-mode-word",children:z(t,G).word},z(t,G).kind),e.jsx("span",{className:"bw-status-text",children:t.coverage.archived===0?t.settings.enabled?"No episodes recorded yet":"No episodes on disk":`${t.coverage.archived} ${t.coverage.archived===1?"episode":"episodes"} on disk`}),e.jsx("button",{className:`glyph-btn bw-gear${F?" on":""}`,onClick:()=>J(s=>!s),"aria-expanded":F,"aria-label":"Settings",title:"Settings",children:e.jsxs("svg",{width:"13",height:"13",viewBox:"0 0 14 14",fill:"none",stroke:"currentColor",strokeWidth:"1.3","aria-hidden":!0,children:[e.jsx("circle",{cx:"7",cy:"7",r:"2.1"}),e.jsx("path",{d:"M7 1.2v1.6M7 11.2v1.6M1.2 7h1.6M11.2 7h1.6M2.9 2.9l1.1 1.1M10 10l1.1 1.1M11.1 2.9L10 4M4 10l-1.1 1.1"})]})}),e.jsxs("label",{className:"bw-switch",htmlFor:"bw-enabled",children:[e.jsx("span",{className:"bw-switch-label",id:"bw-enabled-label",children:"Watch browser activity"}),e.jsx("button",{id:"bw-enabled",type:"button",role:"switch","aria-checked":t.settings.enabled,"aria-labelledby":"bw-enabled-label",className:"switch",onClick:()=>void $({enabled:!t.settings.enabled}),title:t.settings.enabled?"On — every episode it finds is written down, so the list outlives the browsing history being cleared":"Off — showing only what this deck has seen since it started. Anything an earlier run archived is hidden until you switch back on, and nothing new is kept",children:e.jsx("span",{className:"switch-knob"})})]})]})]})]})})}function be({relay:n}){const[i,o]=l.useState(!1),[r,b]=l.useState(!1),t=n.killswitch.blocked?"unblock":"block",w=n.command[t],a=l.useCallback(async()=>{await ae(w.command)&&(b(!0),window.setTimeout(()=>b(!1),1600))},[w.command]),v=n.profiles.filter(d=>{var N;return(N=d.report)==null?void 0:N.present}),y=n.profiles.filter(d=>d.report===null);return e.jsxs("section",{className:"bw-sec",children:[e.jsx("h4",{className:"bw-sec-head",children:"Remote control"}),e.jsx("p",{className:`bw-rc-verdict ${n.verdict}`,role:"status",children:n.verdict==="exposed"?"Claude in Chrome is live here, so any Claude Code session signed in to your Anthropic account can drive this browser — from any machine, with no prompt on this one.":n.verdict==="protected"?`The relay is black-holed in ${n.hostsPath}, so no new connection to it can be made from this machine.`:"Claude in Chrome is not installed and enabled in any profile here, so there is nothing for the relay to drive."}),n.killswitch.foreign.length>0&&e.jsxs("p",{className:"bw-rc-foreign",children:["Another entry for this host is already in ",n.hostsPath,", and ccdeck will not touch it:",n.killswitch.foreign.map(d=>e.jsx("code",{className:"bw-path",children:d},d))]}),e.jsxs("button",{className:"bw-why",onClick:()=>o(d=>!d),"aria-expanded":i,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:i?"▾":"▸"})," ",n.verdict==="protected"?"How to undo the block":"What it can reach, and how to stop it"]}),i&&e.jsxs("div",{className:"bw-rc-body",children:[v.length>0&&e.jsx("dl",{className:"bw-key",children:v.map(d=>e.jsxs(l.Fragment,{children:[e.jsxs("dt",{children:[d.name,d.profile==="Default"?"":` · ${d.profile}`]}),e.jsxs("dd",{children:[d.report.enabled?"enabled":"installed but switched off",d.report.allUrls?", can act on every site":", limited to the sites it was granted",d.report.sensitiveApis.length>0&&e.jsxs(e.Fragment,{children:[" — ",d.report.sensitiveApis.join(", ")]})]})]},`${d.browser}/${d.profile}`))}),y.length>0&&e.jsxs("p",{className:"bw-rc-note",children:[y.map(d=>d.name).join(", ")," — its settings file could not be read, so nothing is claimed about it either way."]}),!n.hostsRead&&e.jsxs("p",{className:"bw-rc-note",children:[n.hostsPath," could not be read, so whether the relay is already blocked is unknown."]}),v.some(d=>d.report.allUrls)&&e.jsxs("p",{className:"bw-rc-note",children:["Narrowing the sites in ",e.jsx("code",{className:"bw-path",children:"chrome://extensions"})," does not change this: the grant is held at the browser level and the per-site list is enforced inside the extension, by the extension."]}),e.jsxs("div",{className:"bw-rc-cmd",children:[e.jsx("code",{children:w.command}),e.jsx("button",{className:"btn bw-rc-copy",onClick:a,children:r?"Copied":"Copy"})]}),e.jsx("p",{className:"bw-rc-note",children:w.note})]})]})}export{ve as SEEN_KEY,ue as agoLabel,le as armsIn,fe as default,de as logBytesLabel,ye as unseenEpisodes,ce as untilLabel,he as visitTotals,we as watchTrouble,Z as watchedBrowsers};
1
+ import{r as l,j as e,u as se,h as te,s as ne,i as ae}from"./index-7mOqoJ3Y.js";import{S as ve,k as ye}from"./index-7mOqoJ3Y.js";function ie(n){const i=/^#?([0-9a-f]{6})$/i.exec((n??"").trim());if(!i)return!1;const o=parseInt(i[1],16),r=[o>>16&255,o>>8&255,o&255].map(b=>{const t=b/255;return t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4)});return .2126*r[0]+.7152*r[1]+.0722*r[2]>.5}const V=1e4,re=12e3;function oe({browsers:n,findings:i,watching:o,palette:r}){const b=l.useRef(null),t=l.useRef(null);return t.current={browsers:n,findings:i,watching:o},l.useEffect(()=>{var L;const w=b.current;if(!w)return;const a=w.getContext("2d");if(!a)return;const v=((L=window.matchMedia)==null?void 0:L.call(window,"(prefers-reduced-motion: reduce)").matches)??!1;let y=0,d=!1;const N=()=>{var H;if(d)return;const W=t.current;if(!W)return;const{browsers:D,findings:U,watching:O}=W,B=r["--accent"]||"#7dd3fc",G=r["--line"]||"#1f2229",_=r["--text-dim"]||"#6e727c",P=ie(r["--bg"]),F=P?42:26,J=P?.85:.65,A=Date.now(),k=Math.min(window.devicePixelRatio||1,2),M=w.clientWidth,R=w.clientHeight;(w.width!==M*k||w.height!==R*k)&&(w.width=M*k,w.height=R*k),a.setTransform(k,0,0,k,0,0),a.clearRect(0,0,M,R);const m=M/2,g=R/2,p=Math.min(M,R)/2-6;a.strokeStyle=G,a.lineWidth=1,a.globalAlpha=P?1:.9;for(const h of[.4,.7,1])a.beginPath(),a.arc(m,g,p*h,0,Math.PI*2),a.stroke();a.beginPath(),a.moveTo(m-p,g),a.lineTo(m+p,g),a.moveTo(m,g-p),a.lineTo(m,g+p),a.stroke(),a.globalAlpha=1;const j=(O&&!v?A%V/V:0)*Math.PI*2-Math.PI/2;if(O){const h=(H=a.createConicGradient)==null?void 0:H.call(a,j-.85,m,g);h&&(h.addColorStop(0,"transparent"),h.addColorStop(.85/(Math.PI*2),`color-mix(in srgb, ${B} ${F}%, transparent)`),h.addColorStop(1,"transparent"),a.fillStyle=h,a.beginPath(),a.moveTo(m,g),a.arc(m,g,p,j-.85,j),a.closePath(),a.fill()),a.strokeStyle=B,a.globalAlpha=J,a.beginPath(),a.moveTo(m,g),a.lineTo(m+Math.cos(j)*p,g+Math.sin(j)*p),a.stroke(),a.globalAlpha=1}D.forEach((h,S)=>{const $=S/Math.max(D.length,1)*Math.PI*2-Math.PI/2,T=h.lastReadMs===null?1:Math.min(1,(A-h.lastReadMs)/12e4),E=p*(.42+T*.5),C=m+Math.cos($)*E,I=g+Math.sin($)*E,q=Math.abs((j-$+Math.PI*3)%(Math.PI*2)-Math.PI),K=O?Math.max(0,1-q/.55):0;a.beginPath(),a.arc(C,I,3+K*1.6,0,Math.PI*2),a.fillStyle=h.running?B:_,a.globalAlpha=h.running?.55+K*.45:.35,a.fill(),a.globalAlpha=1});for(const h of U){const S=(A-h.atMs)/re;if(S<0||S>1)continue;const T=Math.max(0,D.findIndex(I=>I.key===h.browser))/Math.max(D.length,1)*Math.PI*2-Math.PI/2,E=m+Math.cos(T)*p*.6,C=g+Math.sin(T)*p*.6;a.beginPath(),a.arc(E,C,4+S*22,0,Math.PI*2),a.strokeStyle=B,a.globalAlpha=(1-S)*.8,a.lineWidth=1.5,a.stroke(),a.globalAlpha=1}y=requestAnimationFrame(N)};return N(),()=>{d=!0,cancelAnimationFrame(y)}},[r]),e.jsx("canvas",{ref:b,className:"bw-radar",role:"img","aria-label":o?`Watching ${n.length} browser${n.length===1?"":"s"}`:"Not watching"})}function le(n,i,o){if(n===null)return null;const r=n+i-o;return r>0?r:null}function ce(n){const i=Math.ceil(n/1e3),o=Math.floor(i/60),r=i%60;return o>0?`${o}m ${r}s`:`${r}s`}function Z(n){return(n??[]).filter(i=>i.installed&&i.profiles>0)}function z(n,i){return i?{kind:"saving",word:"Saving",detail:""}:n.settings.enabled?{kind:"on",word:"Watching",detail:""}:{kind:"off",word:"Paused",detail:"nothing new is recorded"}}function de(n){return!Number.isFinite(n)||n<=0?"empty":n<1024?`${Math.round(n)} B`:n<1024*1024?`${Math.max(1,Math.round(n/1024))} KB`:`${(n/1024/1024).toFixed(1)} MB`}function he(n,i){return n.map(o=>({key:o.key,name:o.name,running:o.running,visits:i.filter(r=>r.browser===o.key).reduce((r,b)=>r+b.programVisits,0)}))}function ue(n,i){if(n===null)return null;const o=Math.max(0,Math.round((i-n)/1e3));if(o<5)return"just now";if(o<60)return`${o} sec ago`;const r=Math.round(o/60);return r<60?`${r} min ago`:`${Math.round(r/60)} hr ago`}function we(n){const i=n.profiles.filter(r=>r.degraded);if(i.length>0){const r=i[0],b=i.length-1;return{kind:"unreadable",text:`${r.name}/${r.profile} could not be read — ${r.reason??"no reason given"}`+(b>0?`, and ${b} more`:"")+". The watch continues on the profiles it can read."}}const o=Z(n.browsers);return o.length===0?{kind:"no-profiles",text:"No browser on this machine has a profile to read. Open one of the browsers below once and it will be watched from then on."}:o.some(r=>r.running)?null:{kind:"none-running",text:"No watched browser is running, so nothing can be navigated right now. Their history is still read, and anything found while they were open is still here."}}function me(n){const i=o=>new Date(o).toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1});return n.count===1||n.endMs-n.startMs<6e4?i(n.startMs):`${i(n.startMs)} → ${i(n.endMs)}`}function pe(n){return new Date(n).toLocaleDateString(void 0,{weekday:"short",day:"numeric",month:"short"})}function X(n){const i=Math.round((n.endMs-n.startMs)/6e4);return i<1?"":`${i} min`}function fe({onClose:n,onSeen:i,onWatching:o,palette:r}){const b=se(n),[t,w]=l.useState(null),[a,v]=l.useState(null),[y,d]=l.useState(null),[N,L]=l.useState(!1),W=l.useRef(!1),[D,U]=l.useState(null),[O,B]=l.useState(null),[G,_]=l.useState(!1),P=l.useRef(!1),[F,J]=l.useState(!1),[A,k]=l.useState(!1),[M,R]=l.useState(!1),[m,g]=l.useState(!1),[p,Y]=l.useState(!1),[j,H]=l.useState(()=>Date.now());l.useEffect(()=>{const s=setInterval(()=>H(Date.now()),1e3);return()=>clearInterval(s)},[]);const h=l.useCallback(async s=>{var c,x;if(!(s&&!te(W.current))){W.current=s,L(!0);try{const u=await fetch(`/api/browser-watch${s?"?refresh=1":""}`);if(!u.ok)throw new Error(`the deck answered ${u.status}`);const f=await u.json();w(f),P.current||U(((c=f==null?void 0:f.settings)==null?void 0:c.quietMinutes)??15),o(((x=f==null?void 0:f.settings)==null?void 0:x.enabled)===!0),v(null)}catch(u){v(u instanceof Error?u.message:String(u))}finally{W.current=!1,L(!1)}}},[o]);l.useEffect(()=>{h(!1)},[h]),l.useEffect(()=>{const s=setInterval(()=>{h(!1)},1e4);return()=>clearInterval(s)},[h]);const S=l.useCallback(async s=>{try{await fetch("/api/browser-watch/dismiss",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({host:s.host,startMs:s.startMs})})}finally{await h(!1)}},[h]),$=l.useCallback(async s=>{P.current=!0,_(!0);try{const c=await fetch("/api/browser-watch",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s)});if(!c.ok)throw new Error(`the deck answered ${c.status}`);await h(!1),d(null)}catch(c){d(c instanceof Error?c.message:String(c))}finally{P.current=!1,_(!1)}},[h]),T=l.useRef(i);T.current=i,l.useEffect(()=>()=>T.current(Date.now()),[]);const E=l.useMemo(()=>{const s=[];for(const c of(t==null?void 0:t.episodes)??[]){const x=pe(c.startMs),u=s[s.length-1];u&&u.label===x?u.episodes.push(c):s.push({label:x,episodes:[c]})}return s},[t]),C=Z(t==null?void 0:t.browsers),I=((t==null?void 0:t.browsers)??[]).filter(s=>!(s.installed&&s.profiles>0)),q=t?we(t):null,K=C.reduce((s,c)=>s+c.profiles,0),Q=t?le(t.coverage.lastHumanMs,t.coverage.quietMs,j):null;return e.jsx("div",{className:"modal-backdrop",onClick:n,role:"presentation",children:e.jsxs("div",{ref:b,className:"modal bw-modal",onClick:s=>s.stopPropagation(),role:"dialog","aria-modal":"true","aria-labelledby":"bw-title",children:[e.jsxs("header",{className:"modal-head",children:[e.jsxs("div",{className:"modal-title",children:[e.jsx("span",{className:"modal-tool-name",id:"bw-title",children:"Browser watch"}),e.jsx("span",{className:"modal-tool-id",children:t?`${K} ${K===1?"profile":"profiles"} · local only`:"local only"})]}),e.jsxs("div",{className:"modal-actions",children:[e.jsx("button",{className:"glyph-btn",onClick:()=>void h(!0),...ne(N),"aria-label":"Re-read every profile now",title:"Re-read every profile now",children:N?"…":"↻"}),e.jsx("button",{className:"glyph-btn",onClick:n,"aria-label":"Close (Esc)",title:"Close (Esc)",children:"×"})]})]}),e.jsxs("div",{className:"modal-body bw-body",children:[!t&&!a&&e.jsxs("div",{className:"bw-loading",children:[e.jsx("span",{className:"bw-loading-bar","aria-hidden":!0}),e.jsx("p",{children:"Reading each browser's history. The first look copies the file, which takes a moment."})]}),a&&e.jsx("div",{className:"bw-state",children:e.jsxs("div",{className:"bw-row err",children:[e.jsx("span",{className:"bw-dot","aria-hidden":!0}),e.jsx("span",{className:"bw-row-label",children:"Could not read"}),e.jsx("span",{className:"bw-row-detail",children:a})]})}),t&&q&&e.jsx("p",{className:`bw-trouble ${q.kind}`,role:"status",children:q.text}),t&&e.jsxs("div",{className:"bw-work",children:[e.jsxs("aside",{className:"bw-side",children:[e.jsxs("section",{className:"bw-sec",children:[e.jsxs("h4",{className:"bw-sec-head",children:["Activity overview",e.jsx("button",{className:"bw-help",onClick:()=>R(s=>!s),"aria-expanded":M,"aria-label":"What the sweep and the dots mean",children:"?"})]}),e.jsxs("div",{className:"bw-glance",children:[e.jsx(oe,{browsers:C.map(s=>{var c;return{key:s.key,name:s.name,running:s.running,lastReadMs:((c=t.profiles.find(x=>x.browser===s.key))==null?void 0:c.lastWrittenMs)??null}}),findings:t.episodes.slice(0,6).map(s=>({browser:s.browser??null,atMs:s.endMs})),watching:t.settings.enabled,palette:r}),e.jsx("ul",{className:"bw-counts",children:he(C,t.profiles).map(s=>e.jsxs("li",{children:[e.jsx("span",{className:"bw-count-n",children:s.visits.toLocaleString("en-US")}),e.jsx("span",{className:"bw-count-of",children:s.name})]},s.key))})]}),e.jsx("p",{className:"bw-since",children:"Pages a program opened, since this deck started"}),t.coverage.checkedMs>0&&e.jsxs("p",{className:"bw-since",children:["Last checked ",ue(t.coverage.checkedMs,j),t.coverage.checks>0&&e.jsxs(e.Fragment,{children:[" · ",t.coverage.checks.toLocaleString("en-US")," ",t.coverage.checks===1?"check":"checks"]})]}),Q!==null&&e.jsxs("p",{className:"bw-gate",children:["You are browsing — a program page would count in ",ce(Q)]}),M&&e.jsxs("dl",{className:"bw-key",children:[e.jsx("dt",{children:"Sweep"}),e.jsx("dd",{children:"one poll of every watched profile"}),e.jsx("dt",{children:"Dot"}),e.jsx("dd",{children:"a browser, further out the longer since it last wrote history"}),e.jsx("dt",{children:"Ring"}),e.jsx("dd",{children:"a finding, leaving the browser it came from"})]})]}),e.jsxs("section",{className:"bw-sec",children:[e.jsxs("h4",{className:"bw-sec-head",children:["Watched profiles",e.jsx("button",{className:"bw-help",onClick:()=>g(s=>!s),"aria-expanded":m,"aria-label":"What running, idle and connected to Anthropic mean",children:"?"})]}),m&&e.jsxs("dl",{className:"bw-key",children:[e.jsx("dt",{children:"Running"}),e.jsx("dd",{children:"the browser has a process on this machine right now"}),e.jsx("dt",{children:"Connected"}),e.jsx("dd",{children:"an open connection to an address Anthropic's relay uses — which it shares with claude.ai, so an open tab looks the same as an agent channel"})]}),e.jsx("ul",{className:"bw-profiles",children:C.map(s=>e.jsxs("li",{children:[e.jsx("span",{className:`bw-prof-dot${s.running?" on":""}`,"aria-hidden":!0}),e.jsx("span",{className:"bw-prof-name",children:s.name}),e.jsx("span",{className:"bw-prof-state",children:s.running?"running":"idle"}),s.running&&s.relay.state==="live"&&e.jsx("span",{className:"bw-relay",children:"connected to Anthropic"})]},s.key))}),I.length>0&&e.jsxs(e.Fragment,{children:[e.jsxs("button",{className:"bw-why bw-rest-head",onClick:()=>Y(s=>!s),"aria-expanded":p,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:p?"▾":"▸"})," ",I.length," not watched"]}),p&&e.jsxs("p",{className:"bw-rest",children:[I.map(s=>s.name).join(", ")," — not installed, or installed and never opened, so there is no history to read."]})]})]}),t.relay&&e.jsx(be,{relay:t.relay})]}),e.jsxs("div",{className:"bw-main",children:[e.jsxs("section",{className:"bw-findings",children:[e.jsxs("h4",{className:"bw-sec-head bw-head-row",children:["Findings",t.episodes.length>0&&e.jsxs("span",{className:"bw-feed-count",children:[t.episodes.length," ",t.episodes.length===1?"episode":"episodes"]})]}),e.jsx("div",{className:"bw-eps",children:E.length===0?e.jsxs("div",{className:"bw-empty",children:[e.jsx("p",{className:"bw-empty-head",children:"Nothing found yet"}),e.jsxs("p",{className:"bw-empty-note",children:["An episode lands here when a program opens pages in a browser nobody has touched for"," ",t.settings.quietMinutes," ",t.settings.quietMinutes===1?"minute":"minutes",". On most machines that is rare, so an empty list is the ordinary result rather than a sign something is wrong."]}),!t.settings.enabled&&e.jsx("p",{className:"bw-empty-note",children:"Watching is off, so this shows only what this deck has seen since it started. Anything an earlier run recorded comes back when you switch it on."})]}):E.map(s=>e.jsxs("div",{className:"bw-day",children:[e.jsx("h4",{className:"bw-sec-head",children:s.label}),s.episodes.map(c=>{const x=`${c.host}-${c.startMs}`,u=O===x;return e.jsxs("div",{className:`bw-ep${u?" open":""}`,children:[e.jsxs("div",{className:"bw-ep-row",children:[e.jsxs("button",{className:"bw-ep-head",onClick:()=>B(u?null:x),"aria-expanded":u,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:u?"▾":"▸"}),e.jsx("span",{className:"bw-ep-host",children:c.host}),e.jsxs("span",{className:"bw-ep-meta",children:[me(c),X(c)&&e.jsxs(e.Fragment,{children:[" · ",X(c)]})]}),e.jsxs("span",{className:"bw-ep-count",children:[c.count," ",c.count===1?"page":"pages"]})]}),e.jsx("button",{className:"glyph-btn bw-ep-x",onClick:()=>void S(c),"aria-label":`Dismiss ${c.host}`,title:"Dismiss — it leaves this list for good, and stays in the log file",children:"×"})]}),u&&e.jsx("ul",{className:"bw-urls",children:c.urls.map((f,ee)=>e.jsxs("li",{children:[e.jsx("span",{className:"bw-url-time",children:new Date(f.timeMs).toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1})}),e.jsx("span",{className:"bw-url",children:f.url})]},`${f.url}-${f.timeMs}-${ee}`))})]},x)})]},s.label))})]}),e.jsxs("section",{className:"bw-feed",children:[e.jsxs("h4",{className:"bw-sec-head bw-head-row",children:["Live activity",e.jsxs("span",{className:"bw-feed-count",children:[t.log.length," ",t.log.length===1?"event":"events"]})]}),e.jsx("p",{className:"bw-since bw-feed-note",children:"Pages you opened yourself — counted to run the rule, never written down. Addresses appear under Findings, and only for pages a program opened."}),e.jsx("div",{className:"bw-log","aria-live":"polite","aria-relevant":"additions",children:t.log.length===0?e.jsx("p",{className:"bw-note",children:"The deck writes a line here each time it looks at a profile."}):t.log.map(s=>e.jsxs("div",{className:`bw-log-line ${s.level}${s.parts?"":" sys"}`,children:[e.jsx("span",{className:"bw-log-time",children:new Date(s.atMs).toLocaleTimeString("en-GB",{hour12:!1})}),s.parts?e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"bw-log-browser",children:s.parts.browser}),e.jsx("span",{className:"bw-log-profile",title:s.parts.profile,children:s.parts.profile}),e.jsxs("span",{className:"bw-log-value",children:[s.parts.value,s.parts.flagged>0&&e.jsxs("span",{className:"bw-log-flag",children:[" · ",s.parts.flagged," flagged"]})]})]}):e.jsx("span",{className:"bw-log-sys",children:s.text})]},`${s.atMs}-${s.level}-${s.text}`))})]})]})]}),t&&F&&e.jsxs("div",{className:"bw-settings",children:[e.jsxs("label",{title:"A program opening a page counts as a finding only if nobody had touched the browser for this long. Shorter catches more and reports more of your own work; longer is quieter.",children:[e.jsx("span",{children:"Nobody browsing for"}),e.jsxs("select",{value:D??t.settings.quietMinutes,onChange:s=>{U(Number(s.target.value)),$({quietMinutes:Number(s.target.value)})},children:[e.jsx("option",{value:1,children:"1 min"}),e.jsx("option",{value:5,children:"5 min"}),e.jsx("option",{value:15,children:"15 min"}),e.jsx("option",{value:30,children:"30 min"}),e.jsx("option",{value:60,children:"60 min"})]})]}),e.jsxs("label",{children:[e.jsx("span",{children:"When it finds one"}),e.jsx("select",{value:t.settings.reaction,onChange:s=>void $({reaction:s.target.value}),disabled:!t.settings.enabled,title:t.settings.enabled?"What to do besides writing it down.":"Turn watching on to arm a reaction.",children:(t.reactions??["notify"]).map(s=>e.jsx("option",{value:s,children:s==="notify"?"notify me":s==="close-tab"?"close the tab":"quit the browser"},s))})]}),e.jsxs("div",{className:"bw-settings-note",children:[e.jsxs("p",{children:["Chrome marks a navigation that came from an extension or a command rather than from a click. These are the ones that happened while nobody had touched the browser for the time above. ",e.jsx("strong",{children:"Usually that is your own agent doing what you asked."})]}),e.jsxs("button",{className:"bw-why",onClick:()=>k(s=>!s),"aria-expanded":A,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:A?"▾":"▸"})," What Browser Watch can access"]}),A&&e.jsxs("dl",{className:"bw-access",children:[e.jsx("dt",{children:"Reads"}),e.jsxs("dd",{children:["A copy of each browser's own history database — the live file is locked while the browser holds it. Only rows newer than the moment this deck started:"," ",new Date(t.coverage.startedMs).toLocaleString(),"."]}),e.jsx("dt",{children:"Keeps"}),e.jsxs("dd",{children:["Only while the switch is on, and only the episodes it flagged — never your ordinary browsing. In ",e.jsx("code",{className:"bw-path",children:t.coverage.logPath}),typeof t.coverage.logBytes=="number"?` (${de(t.coverage.logBytes)})`:"",", with every address written in full so you can check it yourself."]}),e.jsx("dt",{children:"Sends"}),e.jsx("dd",{children:"Nothing. No part of this reads or writes over the network; the deck serves on 127.0.0.1 and this panel talks only to it."}),e.jsx("dt",{children:"Never reads"}),e.jsx("dd",{children:"Anything from before this deck started, cookies, saved passwords, page contents, or any browser profile with no history file."})]})]})]}),y&&e.jsx("div",{className:"bw-state bw-write-err",role:"alert",children:e.jsxs("div",{className:"bw-row err",children:[e.jsx("span",{className:"bw-dot","aria-hidden":!0}),e.jsx("span",{className:"bw-row-label",children:"Could not save that setting"}),e.jsxs("span",{className:"bw-row-detail",children:[y," — the switches below show what is stored, not what you pressed."]}),e.jsx("button",{className:"glyph-btn bw-write-err-x",onClick:()=>d(null),"aria-label":"Dismiss",title:"Dismiss",children:"×"})]})}),t&&e.jsxs("footer",{className:"bw-status",children:[e.jsx("span",{className:`bw-mode-dot${t.settings.enabled?" on":""}`,"aria-hidden":!0}),e.jsx("span",{className:"bw-mode-word",children:z(t,G).word},z(t,G).kind),e.jsx("span",{className:"bw-status-text",children:t.coverage.archived===0?t.settings.enabled?"No episodes recorded yet":"No episodes on disk":`${t.coverage.archived} ${t.coverage.archived===1?"episode":"episodes"} on disk`}),e.jsx("button",{className:`glyph-btn bw-gear${F?" on":""}`,onClick:()=>J(s=>!s),"aria-expanded":F,"aria-label":"Settings",title:"Settings",children:e.jsxs("svg",{width:"13",height:"13",viewBox:"0 0 14 14",fill:"none",stroke:"currentColor",strokeWidth:"1.3","aria-hidden":!0,children:[e.jsx("circle",{cx:"7",cy:"7",r:"2.1"}),e.jsx("path",{d:"M7 1.2v1.6M7 11.2v1.6M1.2 7h1.6M11.2 7h1.6M2.9 2.9l1.1 1.1M10 10l1.1 1.1M11.1 2.9L10 4M4 10l-1.1 1.1"})]})}),e.jsxs("label",{className:"bw-switch",htmlFor:"bw-enabled",children:[e.jsx("span",{className:"bw-switch-label",id:"bw-enabled-label",children:"Watch browser activity"}),e.jsx("button",{id:"bw-enabled",type:"button",role:"switch","aria-checked":t.settings.enabled,"aria-labelledby":"bw-enabled-label",className:"switch",onClick:()=>void $({enabled:!t.settings.enabled}),title:t.settings.enabled?"On — every episode it finds is written down, so the list outlives the browsing history being cleared":"Off — showing only what this deck has seen since it started. Anything an earlier run archived is hidden until you switch back on, and nothing new is kept",children:e.jsx("span",{className:"switch-knob"})})]})]})]})]})})}function be({relay:n}){const[i,o]=l.useState(!1),[r,b]=l.useState(!1),t=n.killswitch.blocked?"unblock":"block",w=n.command[t],a=l.useCallback(async()=>{await ae(w.command)&&(b(!0),window.setTimeout(()=>b(!1),1600))},[w.command]),v=n.profiles.filter(d=>{var N;return(N=d.report)==null?void 0:N.present}),y=n.profiles.filter(d=>d.report===null);return e.jsxs("section",{className:"bw-sec",children:[e.jsx("h4",{className:"bw-sec-head",children:"Remote control"}),e.jsx("p",{className:`bw-rc-verdict ${n.verdict}`,role:"status",children:n.verdict==="exposed"?"Claude in Chrome is live here, so any Claude Code session signed in to your Anthropic account can drive this browser — from any machine, with no prompt on this one.":n.verdict==="protected"?`The relay is black-holed in ${n.hostsPath}, so no new connection to it can be made from this machine.`:"Claude in Chrome is not installed and enabled in any profile here, so there is nothing for the relay to drive."}),n.killswitch.foreign.length>0&&e.jsxs("p",{className:"bw-rc-foreign",children:["Another entry for this host is already in ",n.hostsPath,", and ccdeck will not touch it:",n.killswitch.foreign.map(d=>e.jsx("code",{className:"bw-path",children:d},d))]}),e.jsxs("button",{className:"bw-why",onClick:()=>o(d=>!d),"aria-expanded":i,children:[e.jsx("span",{className:"bw-chev","aria-hidden":!0,children:i?"▾":"▸"})," ",n.verdict==="protected"?"How to undo the block":"What it can reach, and how to stop it"]}),i&&e.jsxs("div",{className:"bw-rc-body",children:[v.length>0&&e.jsx("dl",{className:"bw-key",children:v.map(d=>e.jsxs(l.Fragment,{children:[e.jsxs("dt",{children:[d.name,d.profile==="Default"?"":` · ${d.profile}`]}),e.jsxs("dd",{children:[d.report.enabled?"enabled":"installed but switched off",d.report.allUrls?", can act on every site":", limited to the sites it was granted",d.report.sensitiveApis.length>0&&e.jsxs(e.Fragment,{children:[" — ",d.report.sensitiveApis.join(", ")]})]})]},`${d.browser}/${d.profile}`))}),y.length>0&&e.jsxs("p",{className:"bw-rc-note",children:[y.map(d=>d.name).join(", ")," — its settings file could not be read, so nothing is claimed about it either way."]}),!n.hostsRead&&e.jsxs("p",{className:"bw-rc-note",children:[n.hostsPath," could not be read, so whether the relay is already blocked is unknown."]}),v.some(d=>d.report.allUrls)&&e.jsxs("p",{className:"bw-rc-note",children:["Narrowing the sites in ",e.jsx("code",{className:"bw-path",children:"chrome://extensions"})," does not change this: the grant is held at the browser level and the per-site list is enforced inside the extension, by the extension."]}),e.jsxs("div",{className:"bw-rc-cmd",children:[e.jsx("code",{children:w.command}),e.jsx("button",{className:"btn bw-rc-copy",onClick:a,children:r?"Copied":"Copy"})]}),e.jsx("p",{className:"bw-rc-note",children:w.note})]})]})}export{ve as SEEN_KEY,ue as agoLabel,le as armsIn,fe as default,de as logBytesLabel,ye as unseenEpisodes,ce as untilLabel,he as visitTotals,we as watchTrouble,Z as watchedBrowsers};
@@ -1 +1 @@
1
- import{a as z,f as j,r as c,u as J,j as e,b as K,s as D,e as Q,c as X,d as x,g as F,h as Y,p as Z}from"./index-CsHTYwoD.js";function _(){let t=0;return{begin(){const a=++t;return()=>a===t},cancel(){t++}}}function ee(t){return!t.answer||t.answer.range!==t.want?{phase:"busy",stale:!1}:t.answer.ok?{phase:t.answer.days>0?"chart":"empty",stale:t.loading}:{phase:"error",stale:t.loading}}function I(t){const a=new Map;for(const n of t)for(const o of n.agents??[]){if(!o||typeof o.agent!="string"||o.agent==="")continue;const r=a.get(o.agent)??{id:o.agent,cost:0,tokens:0};r.cost+=Number.isFinite(o.totalCost)?o.totalCost:0,r.tokens+=Number.isFinite(o.totalTokens)?o.totalTokens:0,a.set(o.agent,r)}return[...a.values()].sort((n,o)=>o.cost-n.cost||n.id.localeCompare(o.id))}function se(t){const a=I([{agents:t}]);return a.length<2?null:a.map(n=>`${z(n.id)} ${j(n.cost)}`).join(" · ")}function U(t){switch(t.toLowerCase()){case"claude":return"var(--usage-purple)";case"codex":return"var(--usage-orange)";case"gemini":return"var(--usage-indigo)";case"copilot":return"var(--usage-green)";default:return"var(--usage-zinc)"}}function ae(t,a){if(!(a>0)||!(t>0))return"0%";const n=t/a*100;return n<.1?"<0.1%":`${n.toFixed(1)}%`}const d=t=>typeof t=="number"&&Number.isFinite(t)?t:0,L=t=>typeof t=="string"?t:"",O=t=>Array.isArray(t)?t.filter(a=>typeof a=="string"):[];function te(t){const a=t??{};return{modelName:L(a.modelName),cost:d(a.cost),inputTokens:d(a.inputTokens),outputTokens:d(a.outputTokens),cacheCreationTokens:d(a.cacheCreationTokens),cacheReadTokens:d(a.cacheReadTokens)}}function ne(t){const a=t??{},n=Array.isArray(a.agents)?a.agents.map(o=>{const r=o??{};return{agent:L(r.agent),totalCost:d(r.totalCost),totalTokens:d(r.totalTokens)}}):void 0;return{period:L(a.period),totalCost:d(a.totalCost),totalTokens:d(a.totalTokens),inputTokens:d(a.inputTokens),outputTokens:d(a.outputTokens),cacheCreationTokens:d(a.cacheCreationTokens),cacheReadTokens:d(a.cacheReadTokens),modelsUsed:O(a.modelsUsed),modelBreakdowns:Array.isArray(a.modelBreakdowns)?a.modelBreakdowns.map(te):[],...n?{agents:n}:{},...a.metadata&&typeof a.metadata=="object"?{metadata:{agents:O(a.metadata.agents)}}:{}}}function oe(t){const a=t??{};return{...a,ok:a.ok===!0,days:Array.isArray(a.days)?a.days.map(ne):void 0}}function M(t){const a=t.toLowerCase();return a.includes("opus")?"var(--usage-purple)":a.includes("sonnet")?"var(--usage-blue)":a.includes("haiku")?"var(--usage-green)":a.includes("gpt-5")||a.includes("gpt5")?"var(--usage-amber)":a.includes("gpt")?"var(--usage-red)":a.includes("gemini")?"var(--usage-indigo)":a.includes("codex")?"var(--usage-orange)":"var(--usage-zinc)"}const le=[7,14,30,90];function re(t){const[a,n]=c.useState(null),[o,r]=c.useState(!1),C=c.useState(_)[0],h=c.useRef(!1),f=(y=!1)=>{if(y&&!Y(h.current))return;const b=C.begin();h.current=y;const T=t;r(!0);const i=`/api/ccusage?since=${Z(T)}${y?"&refresh=1":""}`;fetch(i).then(v=>v.json()).then(v=>{b()&&n({range:T,resp:oe(v)})}).catch(()=>{b()&&n({range:T,resp:{ok:!1,reason:"unreachable"}})}).finally(()=>{b()&&(h.current=!1,r(!1))})};return c.useEffect(()=>(f(!1),()=>C.cancel()),[t]),{landed:a,loading:o,reload:()=>f(!0)}}function ie({onClose:t,providers:a}){var P;const[n,o]=c.useState(30),[r,C]=c.useState(null),{landed:h,loading:f,reload:y}=re(n),b=c.useRef(null),T=J(t,{focusRef:b}),g=ee({loading:f,want:n,answer:h&&{range:h.range,ok:h.resp.ok===!0,days:((P=h.resp.days)==null?void 0:P.length)??0}}),i=g.phase==="chart"?h.resp.days??[]:[],v=c.useMemo(()=>i.reduce((s,l)=>Math.max(s,l.totalCost),0),[i]),B=c.useRef(null);c.useLayoutEffect(()=>{const s=B.current;s&&(s.scrollLeft=s.scrollWidth)},[i]);const{totalCost:R,totalTok:q,inOut:G,cacheRead:H,modelCosts:E}=c.useMemo(()=>{let s=0,l=0,k=0,p=0;const w=new Map;for(const m of i){s+=m.totalCost,l+=m.totalTokens,k+=m.inputTokens+m.outputTokens,p+=m.cacheReadTokens;for(const N of m.modelBreakdowns)w.set(N.modelName,(w.get(N.modelName)??0)+N.cost)}return{totalCost:s,totalTok:l,inOut:k,cacheRead:p,modelCosts:w}},[i]),V=c.useMemo(()=>Array.from(E.entries()).sort((s,l)=>l[1]-s[1]),[E]),$=c.useMemo(()=>I(i),[i]),W=$.length>1,u=r?i.find(s=>s.period===r)??null:null;return e.jsx("div",{className:"modal-backdrop",onClick:t,role:"presentation",children:e.jsxs("div",{ref:T,className:"modal uh-modal",onClick:s=>s.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Usage history",children:[e.jsxs("header",{className:"uh-head",children:[e.jsxs("div",{className:"uh-titlewrap",children:[e.jsx("div",{className:"uh-title",children:"Usage history"}),e.jsx("div",{className:"uh-sub",children:K(a,$.map(s=>s.id))})]}),e.jsx("div",{className:"uh-range",role:"group","aria-label":"Range",children:le.map(s=>e.jsxs("button",{type:"button","aria-pressed":n===s,className:"uh-range-btn",onClick:()=>{o(s),C(null)},children:[s,"d"]},s))}),e.jsx("button",{className:"glyph-btn uh-reload",onClick:y,...D(f),title:"Re-run ccusage","aria-label":"Reload",children:f?"…":"↻"}),e.jsx("button",{ref:b,className:"glyph-btn uh-close",onClick:t,"aria-label":"Close",children:"×"})]}),g.phase==="busy"?e.jsxs("div",{className:"uh-status uh-status-wait","aria-busy":"true",children:[e.jsx("span",{className:"uh-wait-word",children:h?"running ccusage…":"running ccusage… (first run downloads the package)"}),e.jsx("span",{className:"uh-wait-axis","aria-hidden":!0})]}):g.phase==="error"?e.jsxs("div",{className:"uh-status uh-err",title:X(h.resp)||void 0,children:[Q(h.resp,"ccusage did not report usage"),e.jsx("div",{children:e.jsx("button",{className:"btn uh-retry",onClick:y,...D(f),children:f?"trying…":"Try again"})})]}):g.phase==="empty"?e.jsx("div",{className:`uh-status${g.stale?" uh-stale":""}`,"aria-busy":g.stale||void 0,children:"no usage in this range"}):e.jsxs("div",{className:g.stale?"uh-stale":void 0,"aria-busy":g.stale||void 0,children:[e.jsxs("div",{className:"uh-totals",children:[e.jsx(S,{label:"total cost",val:j(R),accent:!0}),e.jsx(S,{label:"tokens",val:x(q)}),e.jsx(S,{label:"input+output",val:x(G)}),e.jsx(S,{label:"cache reads",val:x(H)})]}),W&&e.jsxs("div",{className:"uh-agents",role:"group","aria-label":"Cost by CLI",children:[e.jsx("div",{className:"uh-agent-bar","aria-hidden":"true",children:$.map(s=>e.jsx("span",{className:"uh-agent-seg",style:{width:`${R>0?s.cost/R*100:0}%`,background:U(s.id)}},s.id))}),e.jsx("div",{className:"uh-agent-keys",children:$.map(s=>e.jsxs("span",{className:"uh-agent-key",title:`${s.id} · ${x(s.tokens)} tokens`,children:[e.jsx("span",{className:"uh-legend-dot",style:{background:U(s.id)}}),z(s.id),e.jsx("span",{className:"uh-agent-cost",children:j(s.cost)}),e.jsx("span",{className:"uh-agent-share",children:ae(s.cost,R)})]},s.id))})]}),e.jsx("div",{ref:B,className:"uh-chart",role:"group","aria-label":"Daily cost by model",children:i.map((s,l)=>{const k=v>0?s.totalCost/v*100:0,p=s.period===r,w=`${s.period}, ${j(s.totalCost)}`;return e.jsxs("button",{className:`uh-bar-col${p?" sel":""}`,onClick:()=>C(p?null:s.period),"aria-pressed":p,"aria-label":w,title:`${s.period} · ${j(s.totalCost)}`,style:{flexBasis:`${100/i.length}%`,"--uh-i":l,"--uh-step":`${Math.min(10,200/Math.max(i.length,1))}ms`},children:[e.jsx("div",{className:"uh-bar",style:{height:`${Math.max(k,s.totalCost>0?2:0)}%`},children:s.modelBreakdowns.slice().sort((m,N)=>N.cost-m.cost).map(m=>{const N=s.totalCost>0?m.cost/s.totalCost*100:0;return e.jsx("div",{className:"uh-bar-seg",style:{height:`${N}%`,background:M(m.modelName)}},m.modelName)})}),e.jsx("span",{className:"uh-bar-label",children:s.period.slice(5)})]},s.period)})}),e.jsx("div",{className:"uh-legend",children:V.map(([s,l])=>e.jsxs("span",{className:"uh-legend-item",title:s,children:[e.jsx("span",{className:"uh-legend-dot",style:{background:M(s)}}),F(s)," ",e.jsx("span",{className:"uh-legend-cost",children:j(l)})]},s))}),u&&e.jsxs("div",{className:"uh-detail",children:[e.jsxs("div",{className:"uh-detail-head",children:[e.jsx("span",{className:"uh-detail-date",children:u.period}),e.jsx("span",{className:"uh-detail-cost",children:j(u.totalCost)}),(()=>{var k,p;const l=se(u.agents)??((p=(k=u.metadata)==null?void 0:k.agents)!=null&&p.length?u.metadata.agents.join(" · "):null);return l?e.jsx("span",{className:"uh-detail-agents",title:l,children:l}):null})()]}),e.jsxs("div",{className:"uh-detail-mini",children:[e.jsx(A,{label:"input",val:x(u.inputTokens)}),e.jsx(A,{label:"output",val:x(u.outputTokens)}),e.jsx(A,{label:"cache write",val:x(u.cacheCreationTokens)}),e.jsx(A,{label:"cache read",val:x(u.cacheReadTokens)})]}),e.jsx("div",{className:"uh-detail-models",children:u.modelBreakdowns.slice().sort((s,l)=>l.cost-s.cost).map(s=>{const l=u.totalCost>0?s.cost/u.totalCost*100:0;return e.jsxs("div",{className:"uh-model-row",title:s.modelName,children:[e.jsxs("span",{className:"uh-model-name",children:[e.jsx("span",{className:"uh-legend-dot",style:{background:M(s.modelName)}}),e.jsx("span",{className:"uh-model-label",children:F(s.modelName)})]}),e.jsx("span",{className:"uh-model-bar",children:e.jsx("span",{className:"uh-model-bar-fill",style:{width:`${l}%`,background:M(s.modelName)}})}),e.jsx("span",{className:"uh-model-cost",children:j(s.cost)})]},s.modelName)})})]})]})]})})}function S({label:t,val:a,accent:n}){return e.jsxs("div",{className:"uh-stat",children:[e.jsx("span",{className:`uh-stat-val${n?" accent":""}`,children:a}),e.jsx("span",{className:"uh-stat-label",children:t})]})}function A({label:t,val:a}){return e.jsxs("div",{className:"uh-ministat",children:[e.jsx("span",{className:"uh-ministat-val",children:a}),e.jsx("span",{className:"uh-ministat-label",children:t})]})}export{ie as default};
1
+ import{a as z,f as j,r as c,u as J,j as e,b as K,s as D,e as Q,c as X,d as x,g as F,h as Y,p as Z}from"./index-7mOqoJ3Y.js";function _(){let t=0;return{begin(){const a=++t;return()=>a===t},cancel(){t++}}}function ee(t){return!t.answer||t.answer.range!==t.want?{phase:"busy",stale:!1}:t.answer.ok?{phase:t.answer.days>0?"chart":"empty",stale:t.loading}:{phase:"error",stale:t.loading}}function I(t){const a=new Map;for(const n of t)for(const o of n.agents??[]){if(!o||typeof o.agent!="string"||o.agent==="")continue;const r=a.get(o.agent)??{id:o.agent,cost:0,tokens:0};r.cost+=Number.isFinite(o.totalCost)?o.totalCost:0,r.tokens+=Number.isFinite(o.totalTokens)?o.totalTokens:0,a.set(o.agent,r)}return[...a.values()].sort((n,o)=>o.cost-n.cost||n.id.localeCompare(o.id))}function se(t){const a=I([{agents:t}]);return a.length<2?null:a.map(n=>`${z(n.id)} ${j(n.cost)}`).join(" · ")}function U(t){switch(t.toLowerCase()){case"claude":return"var(--usage-purple)";case"codex":return"var(--usage-orange)";case"gemini":return"var(--usage-indigo)";case"copilot":return"var(--usage-green)";default:return"var(--usage-zinc)"}}function ae(t,a){if(!(a>0)||!(t>0))return"0%";const n=t/a*100;return n<.1?"<0.1%":`${n.toFixed(1)}%`}const d=t=>typeof t=="number"&&Number.isFinite(t)?t:0,L=t=>typeof t=="string"?t:"",O=t=>Array.isArray(t)?t.filter(a=>typeof a=="string"):[];function te(t){const a=t??{};return{modelName:L(a.modelName),cost:d(a.cost),inputTokens:d(a.inputTokens),outputTokens:d(a.outputTokens),cacheCreationTokens:d(a.cacheCreationTokens),cacheReadTokens:d(a.cacheReadTokens)}}function ne(t){const a=t??{},n=Array.isArray(a.agents)?a.agents.map(o=>{const r=o??{};return{agent:L(r.agent),totalCost:d(r.totalCost),totalTokens:d(r.totalTokens)}}):void 0;return{period:L(a.period),totalCost:d(a.totalCost),totalTokens:d(a.totalTokens),inputTokens:d(a.inputTokens),outputTokens:d(a.outputTokens),cacheCreationTokens:d(a.cacheCreationTokens),cacheReadTokens:d(a.cacheReadTokens),modelsUsed:O(a.modelsUsed),modelBreakdowns:Array.isArray(a.modelBreakdowns)?a.modelBreakdowns.map(te):[],...n?{agents:n}:{},...a.metadata&&typeof a.metadata=="object"?{metadata:{agents:O(a.metadata.agents)}}:{}}}function oe(t){const a=t??{};return{...a,ok:a.ok===!0,days:Array.isArray(a.days)?a.days.map(ne):void 0}}function M(t){const a=t.toLowerCase();return a.includes("opus")?"var(--usage-purple)":a.includes("sonnet")?"var(--usage-blue)":a.includes("haiku")?"var(--usage-green)":a.includes("gpt-5")||a.includes("gpt5")?"var(--usage-amber)":a.includes("gpt")?"var(--usage-red)":a.includes("gemini")?"var(--usage-indigo)":a.includes("codex")?"var(--usage-orange)":"var(--usage-zinc)"}const le=[7,14,30,90];function re(t){const[a,n]=c.useState(null),[o,r]=c.useState(!1),C=c.useState(_)[0],h=c.useRef(!1),f=(y=!1)=>{if(y&&!Y(h.current))return;const b=C.begin();h.current=y;const T=t;r(!0);const i=`/api/ccusage?since=${Z(T)}${y?"&refresh=1":""}`;fetch(i).then(v=>v.json()).then(v=>{b()&&n({range:T,resp:oe(v)})}).catch(()=>{b()&&n({range:T,resp:{ok:!1,reason:"unreachable"}})}).finally(()=>{b()&&(h.current=!1,r(!1))})};return c.useEffect(()=>(f(!1),()=>C.cancel()),[t]),{landed:a,loading:o,reload:()=>f(!0)}}function ie({onClose:t,providers:a}){var P;const[n,o]=c.useState(30),[r,C]=c.useState(null),{landed:h,loading:f,reload:y}=re(n),b=c.useRef(null),T=J(t,{focusRef:b}),g=ee({loading:f,want:n,answer:h&&{range:h.range,ok:h.resp.ok===!0,days:((P=h.resp.days)==null?void 0:P.length)??0}}),i=g.phase==="chart"?h.resp.days??[]:[],v=c.useMemo(()=>i.reduce((s,l)=>Math.max(s,l.totalCost),0),[i]),B=c.useRef(null);c.useLayoutEffect(()=>{const s=B.current;s&&(s.scrollLeft=s.scrollWidth)},[i]);const{totalCost:R,totalTok:q,inOut:G,cacheRead:H,modelCosts:E}=c.useMemo(()=>{let s=0,l=0,k=0,p=0;const w=new Map;for(const m of i){s+=m.totalCost,l+=m.totalTokens,k+=m.inputTokens+m.outputTokens,p+=m.cacheReadTokens;for(const N of m.modelBreakdowns)w.set(N.modelName,(w.get(N.modelName)??0)+N.cost)}return{totalCost:s,totalTok:l,inOut:k,cacheRead:p,modelCosts:w}},[i]),V=c.useMemo(()=>Array.from(E.entries()).sort((s,l)=>l[1]-s[1]),[E]),$=c.useMemo(()=>I(i),[i]),W=$.length>1,u=r?i.find(s=>s.period===r)??null:null;return e.jsx("div",{className:"modal-backdrop",onClick:t,role:"presentation",children:e.jsxs("div",{ref:T,className:"modal uh-modal",onClick:s=>s.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Usage history",children:[e.jsxs("header",{className:"uh-head",children:[e.jsxs("div",{className:"uh-titlewrap",children:[e.jsx("div",{className:"uh-title",children:"Usage history"}),e.jsx("div",{className:"uh-sub",children:K(a,$.map(s=>s.id))})]}),e.jsx("div",{className:"uh-range",role:"group","aria-label":"Range",children:le.map(s=>e.jsxs("button",{type:"button","aria-pressed":n===s,className:"uh-range-btn",onClick:()=>{o(s),C(null)},children:[s,"d"]},s))}),e.jsx("button",{className:"glyph-btn uh-reload",onClick:y,...D(f),title:"Re-run ccusage","aria-label":"Reload",children:f?"…":"↻"}),e.jsx("button",{ref:b,className:"glyph-btn uh-close",onClick:t,"aria-label":"Close",children:"×"})]}),g.phase==="busy"?e.jsxs("div",{className:"uh-status uh-status-wait","aria-busy":"true",children:[e.jsx("span",{className:"uh-wait-word",children:h?"running ccusage…":"running ccusage… (first run downloads the package)"}),e.jsx("span",{className:"uh-wait-axis","aria-hidden":!0})]}):g.phase==="error"?e.jsxs("div",{className:"uh-status uh-err",title:X(h.resp)||void 0,children:[Q(h.resp,"ccusage did not report usage"),e.jsx("div",{children:e.jsx("button",{className:"btn uh-retry",onClick:y,...D(f),children:f?"trying…":"Try again"})})]}):g.phase==="empty"?e.jsx("div",{className:`uh-status${g.stale?" uh-stale":""}`,"aria-busy":g.stale||void 0,children:"no usage in this range"}):e.jsxs("div",{className:g.stale?"uh-stale":void 0,"aria-busy":g.stale||void 0,children:[e.jsxs("div",{className:"uh-totals",children:[e.jsx(S,{label:"total cost",val:j(R),accent:!0}),e.jsx(S,{label:"tokens",val:x(q)}),e.jsx(S,{label:"input+output",val:x(G)}),e.jsx(S,{label:"cache reads",val:x(H)})]}),W&&e.jsxs("div",{className:"uh-agents",role:"group","aria-label":"Cost by CLI",children:[e.jsx("div",{className:"uh-agent-bar","aria-hidden":"true",children:$.map(s=>e.jsx("span",{className:"uh-agent-seg",style:{width:`${R>0?s.cost/R*100:0}%`,background:U(s.id)}},s.id))}),e.jsx("div",{className:"uh-agent-keys",children:$.map(s=>e.jsxs("span",{className:"uh-agent-key",title:`${s.id} · ${x(s.tokens)} tokens`,children:[e.jsx("span",{className:"uh-legend-dot",style:{background:U(s.id)}}),z(s.id),e.jsx("span",{className:"uh-agent-cost",children:j(s.cost)}),e.jsx("span",{className:"uh-agent-share",children:ae(s.cost,R)})]},s.id))})]}),e.jsx("div",{ref:B,className:"uh-chart",role:"group","aria-label":"Daily cost by model",children:i.map((s,l)=>{const k=v>0?s.totalCost/v*100:0,p=s.period===r,w=`${s.period}, ${j(s.totalCost)}`;return e.jsxs("button",{className:`uh-bar-col${p?" sel":""}`,onClick:()=>C(p?null:s.period),"aria-pressed":p,"aria-label":w,title:`${s.period} · ${j(s.totalCost)}`,style:{flexBasis:`${100/i.length}%`,"--uh-i":l,"--uh-step":`${Math.min(10,200/Math.max(i.length,1))}ms`},children:[e.jsx("div",{className:"uh-bar",style:{height:`${Math.max(k,s.totalCost>0?2:0)}%`},children:s.modelBreakdowns.slice().sort((m,N)=>N.cost-m.cost).map(m=>{const N=s.totalCost>0?m.cost/s.totalCost*100:0;return e.jsx("div",{className:"uh-bar-seg",style:{height:`${N}%`,background:M(m.modelName)}},m.modelName)})}),e.jsx("span",{className:"uh-bar-label",children:s.period.slice(5)})]},s.period)})}),e.jsx("div",{className:"uh-legend",children:V.map(([s,l])=>e.jsxs("span",{className:"uh-legend-item",title:s,children:[e.jsx("span",{className:"uh-legend-dot",style:{background:M(s)}}),F(s)," ",e.jsx("span",{className:"uh-legend-cost",children:j(l)})]},s))}),u&&e.jsxs("div",{className:"uh-detail",children:[e.jsxs("div",{className:"uh-detail-head",children:[e.jsx("span",{className:"uh-detail-date",children:u.period}),e.jsx("span",{className:"uh-detail-cost",children:j(u.totalCost)}),(()=>{var k,p;const l=se(u.agents)??((p=(k=u.metadata)==null?void 0:k.agents)!=null&&p.length?u.metadata.agents.join(" · "):null);return l?e.jsx("span",{className:"uh-detail-agents",title:l,children:l}):null})()]}),e.jsxs("div",{className:"uh-detail-mini",children:[e.jsx(A,{label:"input",val:x(u.inputTokens)}),e.jsx(A,{label:"output",val:x(u.outputTokens)}),e.jsx(A,{label:"cache write",val:x(u.cacheCreationTokens)}),e.jsx(A,{label:"cache read",val:x(u.cacheReadTokens)})]}),e.jsx("div",{className:"uh-detail-models",children:u.modelBreakdowns.slice().sort((s,l)=>l.cost-s.cost).map(s=>{const l=u.totalCost>0?s.cost/u.totalCost*100:0;return e.jsxs("div",{className:"uh-model-row",title:s.modelName,children:[e.jsxs("span",{className:"uh-model-name",children:[e.jsx("span",{className:"uh-legend-dot",style:{background:M(s.modelName)}}),e.jsx("span",{className:"uh-model-label",children:F(s.modelName)})]}),e.jsx("span",{className:"uh-model-bar",children:e.jsx("span",{className:"uh-model-bar-fill",style:{width:`${l}%`,background:M(s.modelName)}})}),e.jsx("span",{className:"uh-model-cost",children:j(s.cost)})]},s.modelName)})})]})]})]})})}function S({label:t,val:a,accent:n}){return e.jsxs("div",{className:"uh-stat",children:[e.jsx("span",{className:`uh-stat-val${n?" accent":""}`,children:a}),e.jsx("span",{className:"uh-stat-label",children:t})]})}function A({label:t,val:a}){return e.jsxs("div",{className:"uh-ministat",children:[e.jsx("span",{className:"uh-ministat-val",children:a}),e.jsx("span",{className:"uh-ministat-label",children:t})]})}export{ie as default};