github-stars-organizer 1.1.0 → 1.2.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.
@@ -1,3 +0,0 @@
1
- import{B as e,H as t,J as n,K as r,R as i,V as a,W as o,it as s,n as c,nt as l,q as u,st as d,t as f,z as p}from"./button-C0MQwO-R.js";import{S as m,i as h,n as g,r as _}from"./coerce-D595RIhb.js";import{n as v}from"./star-BoN7NVgv.js";import{C as y,T as b,_ as ee,a as te,b as ne,c as re,d as ie,f as ae,g as x,i as oe,l as se,m as ce,n as S,o as le,p as ue,r as de,s as fe,t as C,u as w,v as pe,w as T,x as E,y as D}from"./registry-DZ4hp3Pl.js";import{d as O,f as k,l as A,o as j,p as me,s as M,t as N,u as he}from"./local-backups-BGSAd5Gu.js";import{t as ge}from"./badge-DWmLT6OI.js";import{n as P,r as F,t as I}from"./routes-C8TmhzRl.js";import{t as L}from"./triangle-alert-Cgw2r-Xm.js";import{c as R,o as _e,r as z,s as B}from"./schemas-B0gCwksU.js";import{n as ve,t as V}from"./local-sync-Dd3hEb9I.js";var ye=i(`chevron-down`,[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]),H=i(`sticky-note`,[[`path`,{d:`M21 9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2z`,key:`1dfntj`}],[`path`,{d:`M15 3v5a1 1 0 0 0 1 1h5`,key:`6s6qgf`}]]),U=d(s(),1);function be(e,t){return{lists:e.map(e=>({id:e.id,github_list_id:e.id,name:e.name,description:e.description,is_private:!1,created_at:e.createdAt,updated_at:e.updatedAt})),list_items:e.flatMap(e=>e.repoGithubIds.flatMap((n,r)=>{let i=t.get(n);return i?[{id:`${e.id}:${i}`,list_id:e.id,repo_id:i,position:r,added_at:e.updatedAt}]:[]}))}}var xe=5;async function Se(e){try{let t=await fetch(`/api/lists?user=${encodeURIComponent(e)}`);return t.ok?(await t.json()).lists:null}catch{return null}}async function Ce(n,r){let i=Se(n),o=[];for(let e=1;e<=xe;e++){let t=await fetch(`https://api.github.com/users/${encodeURIComponent(n)}/starred?per_page=100&page=${e}`,{headers:{Accept:`application/vnd.github.star+json`}});if(t.status===404)throw Error(`No GitHub user called ${n}.`);if(t.status===403||t.status===429)throw Error(`GitHub's limit for signed-out requests is 60 an hour. Try again later, or self-host it.`);if(!t.ok)throw Error(`GitHub returned ${t.status}.`);let i=await t.json();if(o.push(...i),r(`Read ${o.length} stars...`),i.length<100)break}if(o.length===0)throw Error(`${n} hasn't starred anything public.`);let s=new Date().toISOString(),c=o.map(({starred_at:e,repo:t})=>({id:String(t.id),github_id:t.id,node_id:t.node_id,name:t.name,full_name:t.full_name,owner:t.owner.login,description:t.description,url:t.html_url,homepage:t.homepage||null,language:t.language,stars:t.stargazers_count,forks:t.forks_count,topics:t.topics?.length?t.topics.join(`,`):null,starred_at:e,notes:null,usefulness_rating:null,imported_at:s,health_score:null,health_data:null,health_refreshed_at:null,is_archived:t.archived,pushed_at:t.pushed_at,created_at:t.created_at}));for(let n of[t,e,p,a]){let e=[...n.keys()];e.length>0&&n.delete(e)}a.insert(c),r(`Reading Lists...`);let l=await i;if(l===null)return{stars:c.length,lists:`unavailable`};let u=be(l,new Map(c.map(e=>[e.github_id,e.id])));return u.lists.length>0&&p.insert(u.lists),u.list_items.length>0&&e.insert(u.list_items),{stars:c.length,lists:u.lists.length}}var W=l();function we(){let[e,t]=(0,U.useState)(``),[r,i]=(0,U.useState)(n),[a,o]=(0,U.useState)(!1),[s,c]=(0,U.useState)(``);async function l(){let n=e.trim().replace(/^@/,``);if(n){o(!0),c(`Reading stars...`);try{let e=await Ce(n,c);i(n),t(``),c(e.lists===`unavailable`?`${e.stars} recent stars from @${n}. Couldn't read their Lists.`:`${e.stars} recent stars and ${e.lists} Lists from @${n}.`)}catch(e){c(e instanceof Error?e.message:`Couldn't read those stars.`)}finally{o(!1)}}}return(0,W.jsxs)(`div`,{className:`flex flex-col gap-1.5 sm:items-end`,children:[(0,W.jsxs)(`form`,{onSubmit:e=>{e.preventDefault(),l()},className:`flex w-full items-center gap-2 sm:w-auto`,children:[(0,W.jsx)(m,{value:e,onChange:e=>t(e.target.value),placeholder:`Any GitHub username`,"aria-label":`GitHub username`,autoCapitalize:`none`,autoCorrect:`off`,spellCheck:!1,enterKeyHint:`go`,className:`h-10 min-w-0 flex-1 sm:h-9 sm:w-48 sm:flex-none`}),(0,W.jsx)(f,{type:`submit`,className:`h-10 sm:h-9`,disabled:a||!e.trim(),children:a?`Loading...`:`Load`})]}),(0,W.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:s||(r?`Showing @${r}`:``)})]})}var Te=z(B({repo_id:_e().int(),notes:R().nullish(),tags:z(B({name:R()})).default([])})),G=class extends Error{};function Ee(e){let t=Te.safeParse(e);if(!t.success)throw new G(`This file isn't an Astral export. In Astral, open Settings and press Export to get one.`);if(t.data.length===0)throw new G(`This Astral export is empty. Astral only exports stars that have a tag or a note.`);return t.data}var De=`Astral tags: `;function Oe(e){let t=e.tags.map(e=>e.name.trim()).filter(Boolean);return[...new Map(t.map(e=>[e.toLowerCase(),e])).values()]}function ke(e,t){let n=e?.trim()??``,r=t.filter(e=>e&&!n.includes(e));return r.length===0?null:[n,...r].filter(Boolean).join(`
2
-
3
- `)}function K(e,t,n){let r=new Map(t.repos.map(e=>[e.github_id,e])),i=new Map(t.lists.map(e=>[e.name.toLowerCase(),e])),a=new Set(t.list_items.map(e=>`${e.list_id}:${e.repo_id}`)),o=new Set([...n].map(e=>e.toLowerCase())),s=new Map,c=[],l=0;for(let t of e){let e=r.get(t.repo_id);if(!e){l++;continue}let n=Oe(t);for(let t of n){let n=t.toLowerCase(),r=s.get(n);r||(r={name:t,existingList:i.get(n)??null,toAdd:[],starred:0},s.set(n,r)),r.starred++,(!r.existingList||!a.has(`${r.existingList.id}:${e.id}`))&&r.toAdd.push(e)}let u=n.filter(e=>!o.has(e.toLowerCase())),d=ke(e.notes,[t.notes?.trim()??``,u.length?`${De}${u.join(`, `)}`:``]);d!==null&&c.push({repo:e,notes:d})}return{tags:[...s.values()].sort((e,t)=>t.starred-e.starred||e.name.localeCompare(t.name)),notes:c,notStarred:l,listSlots:Math.max(0,32-t.lists.length)}}function Ae(e){let t=new Set,n=e.listSlots;for(let r of e.tags)r.existingList?t.add(r.name):n>0&&(t.add(r.name),n--);return t}function q(e,t){return e.tags.filter(e=>t.has(e.name)&&!e.existingList).length}function J(e){return e instanceof Error?e.message:String(e)}async function je(e,t,n,r=()=>{}){let i=e.tags.filter(e=>t.has(e.name)),a=i.reduce((e,t)=>e+t.toAdd.length+ +!t.existingList,0)+e.notes.length,o={listsCreated:0,added:0,notes:0,failed:[]},s=0,c=()=>r({done:++s,total:a});for(let e of i){let t=e.existingList;if(!t){try{t=await n.createList(e.name),o.listsCreated++}catch(t){o.failed.push({what:`List "${e.name}"`,reason:J(t)})}c()}for(let r of e.toAdd){if(t)try{await n.addToList(t,r),o.added++}catch(t){o.failed.push({what:`${r.full_name} in "${e.name}"`,reason:J(t)})}c()}}for(let t of e.notes){try{await n.setNotes(t.repo,t.notes),o.notes++}catch(e){o.failed.push({what:`Notes on ${t.repo.full_name}`,reason:J(e)})}c()}return o}var Me={createList:async e=>{let{githubListId:t}=await r.createList({name:e,description:null}),n=new Date().toISOString();return{id:t,github_list_id:t,name:e,description:null,is_private:!1,created_at:n,updated_at:n}},addToList:async(e,t)=>{if(!t.node_id||!e.github_list_id)throw Error(`Import your stars again first.`);await r.changeMembership({repoNodeId:t.node_id,repoGithubId:t.github_id,githubListId:e.github_list_id,change:`add`})},setNotes:(e,t)=>o(()=>a.update(e.id,e=>{e.notes=t}))};async function Ne(){await N()}async function Y(){await V()}function X(e){return e instanceof Error?e.message:String(e)}function Z({label:e,children:t}){return(0,W.jsxs)(g,{children:[(0,W.jsx)(h,{openOnHover:!0,delay:150,render:(0,W.jsx)(`button`,{type:`button`,"aria-label":e,className:`inline-flex text-muted-foreground hover:text-foreground`}),children:(0,W.jsx)(F,{className:`size-4`})}),(0,W.jsx)(_,{className:`w-64 p-3 text-sm`,children:t})]})}function Q({icon:e,count:t,label:n,className:r}){return(0,W.jsxs)(`span`,{title:n,"aria-label":`${t} ${n}`,className:c(`inline-flex items-center gap-1 tabular-nums [&>svg]:size-4`,r),children:[e,t]})}function Pe({stored:e,onClose:t}){let[n,r]=(0,U.useState)({step:`pick`}),i=(0,U.useRef)(null),a=(0,U.useRef)(null);async function o(t){if(t)try{let n=Ee(JSON.parse(await t.text()));r({step:`review`,stars:n,asLists:Ae(K(n,e,new Set))})}catch(e){r({step:`failed`,message:e instanceof SyntaxError?`${t.name} isn't a JSON file.`:X(e)})}}async function s(t,n){let i=K(t,e,n);r({step:`applying`,done:0,total:0});try{await Ne();let e=await je(i,n,Me,({done:e,total:t})=>r({step:`applying`,done:e,total:t}));await Y(),r({step:`done`,result:e})}catch(e){r({step:`failed`,message:X(e)})}}return(0,W.jsx)(j,{open:!0,onOpenChange:e=>{!e&&n.step!==`applying`&&t()},children:(0,W.jsxs)(M,{initialFocus:a,children:[(0,W.jsx)(he,{children:(0,W.jsxs)(O,{className:`flex items-center gap-2`,children:[`Import from Astral`,(0,W.jsx)(Z,{label:`How the import works`,children:`Each tag becomes a GitHub List. Astral notes go below your own. Tags you uncheck are saved in each repo's notes instead. Nothing changes until you press Import.`})]})}),n.step===`pick`&&(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(`input`,{ref:i,type:`file`,accept:`application/json,.json`,className:`hidden`,onChange:e=>{o(e.target.files?.[0]),e.target.value=``}}),(0,W.jsxs)(f,{ref:a,variant:`outline`,onClick:()=>i.current?.click(),children:[(0,W.jsx)(k,{className:`mr-1.5 size-4`}),`Choose export`]}),(0,W.jsx)(Z,{label:`Where to get the export`,children:`In Astral, open Settings and press Export.`})]}),n.step===`review`&&(0,W.jsx)(Ie,{stars:n.stars,stored:e,asLists:n.asLists,onChange:e=>r({...n,asLists:e})}),n.step===`applying`&&(0,W.jsx)(Le,{done:n.done,total:n.total}),n.step===`done`&&(0,W.jsx)(Re,{result:n.result}),n.step===`failed`&&(0,W.jsx)(`p`,{className:`text-sm text-destructive`,children:n.message}),n.step!==`pick`&&n.step!==`applying`&&(0,W.jsxs)(A,{children:[n.step===`review`&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(f,{variant:`ghost`,onClick:t,children:`Cancel`}),(0,W.jsx)(f,{onClick:()=>void s(n.stars,n.asLists),disabled:!Fe(n.stars,e,n.asLists),children:`Import`})]}),(n.step===`done`||n.step===`failed`)&&(0,W.jsx)(f,{onClick:t,children:`Close`})]})]})})}function Fe(e,t,n){let r=K(e,t,n);return r.notes.length+r.tags.filter(e=>n.has(e.name)&&e.toAdd.length>0).length>0&&q(r,n)<=r.listSlots}function Ie({stars:e,stored:t,asLists:n,onChange:r}){let i=K(e,t,n),a=q(i,n),o=a>i.listSlots;function s(e,t){let i=new Set(n);t?i.add(e):i.delete(e),r(i)}return(0,W.jsxs)(`div`,{className:`space-y-3 text-sm`,children:[i.tags.length>0&&(0,W.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,W.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,W.jsx)(`span`,{className:`font-medium`,children:`Lists`}),(0,W.jsxs)(`span`,{className:c(`flex items-center gap-1.5 text-xs tabular-nums text-muted-foreground`,o&&`text-destructive`),children:[a,`/`,i.listSlots,` new`,(0,W.jsxs)(Z,{label:`GitHub's List limit`,children:[`GitHub allows `,32,` Lists, and you have room for `,i.listSlots,` more. A tag matching a List you have adds to it instead.`]})]})]}),(0,W.jsx)(`ul`,{className:`max-h-64 space-y-1 overflow-y-auto rounded-md border border-border p-2`,children:i.tags.map(e=>(0,W.jsx)(`li`,{children:(0,W.jsxs)(`label`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(E,{checked:n.has(e.name),onCheckedChange:t=>s(e.name,t===!0)}),(0,W.jsx)(`span`,{className:`min-w-0 flex-1 truncate`,children:e.name}),!e.existingList&&(0,W.jsx)(ge,{variant:`outline`,children:`new`}),e.toAdd.length>0?(0,W.jsx)(Q,{icon:(0,W.jsx)(P,{}),count:e.toAdd.length,label:`repos to add`,className:`text-xs text-muted-foreground`}):(0,W.jsx)(`span`,{title:`Already in your List`,"aria-label":`Already in your List`,children:(0,W.jsx)(b,{className:`size-4 text-muted-foreground`})})]})},e.name))})]}),(0,W.jsxs)(`div`,{className:`flex items-center gap-4 text-muted-foreground`,children:[i.notes.length>0&&(0,W.jsx)(Q,{icon:(0,W.jsx)(H,{}),count:i.notes.length,label:`repos getting notes`}),i.notStarred>0&&(0,W.jsx)(Q,{icon:(0,W.jsx)(y,{}),count:i.notStarred,label:`not starred any more, skipped`})]})]})}function Le({done:e,total:t}){return(0,W.jsxs)(`div`,{className:`space-y-1.5`,role:`progressbar`,"aria-valuemin":0,"aria-valuemax":t,"aria-valuenow":e,"aria-label":`Writing to GitHub`,children:[(0,W.jsx)(`div`,{className:`h-1.5 overflow-hidden rounded-full bg-muted`,children:(0,W.jsx)(`div`,{className:`h-full bg-primary transition-[width]`,style:{width:`${t>0?Math.round(e/t*100):0}%`}})}),(0,W.jsxs)(`p`,{className:`text-xs tabular-nums text-muted-foreground`,children:[e,`/`,t]})]})}function Re({result:e}){return(0,W.jsxs)(`div`,{className:`space-y-2 text-sm`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-4`,children:[(0,W.jsx)(Q,{icon:(0,W.jsx)(T,{}),count:e.listsCreated,label:`Lists created`}),(0,W.jsx)(Q,{icon:(0,W.jsx)(P,{}),count:e.added,label:`repos added to Lists`}),(0,W.jsx)(Q,{icon:(0,W.jsx)(H,{}),count:e.notes,label:`repos given notes`})]}),e.failed.length>0&&(0,W.jsxs)(`details`,{className:`rounded-md border border-destructive/40 p-2 text-destructive`,children:[(0,W.jsxs)(`summary`,{className:`flex cursor-pointer items-center gap-1.5`,children:[(0,W.jsx)(L,{className:`size-4`}),e.failed.length,` failed`]}),(0,W.jsx)(`ul`,{className:`mt-1 max-h-40 space-y-0.5 overflow-y-auto text-xs`,children:e.failed.map(e=>(0,W.jsxs)(`li`,{children:[e.what,`: `,e.reason]},e.what))})]})]})}var $=!1;async function ze(e){e(`Reading your stars and Lists from GitHub...`);let{stars:t,lists:n}=await V(),r=`Read ${t} stars and ${n} lists.`;e(r);let i=await ve(`missing`,({scored:t,total:n})=>e(`${r} Scoring health ${t}/${n}...`));i.total>0&&e(`${r} Scored ${i.scored} repos.`)}function Be(){let t=I.useNavigate(),n=I.useSearch(),{data:r}=v(e=>e.from({repoCollection:a}).select(({repoCollection:e})=>e)),{data:i}=v(e=>e.from({listCollection:p}).select(({listCollection:e})=>e)),{data:o}=v(t=>t.from({listItemCollection:e}).select(({listItemCollection:e})=>e)),[s,c]=(0,U.useState)(!1),[l,d]=(0,U.useState)(!1),[m,h]=(0,U.useState)(``),[g,_]=(0,U.useState)(null),y=ce(),b=r??[],T=(0,U.useMemo)(()=>{let e=new Map;for(let t of o??[]){let n=e.get(t.repo_id)??[];n.push(t.list_id),e.set(t.repo_id,n)}return e},[o]),{registry:E,sortDefinitions:O}=(0,U.useMemo)(()=>de(b,{lists:i??[],listIdsByRepo:T}),[b,i,T]),k=(0,U.useMemo)(()=>ie(n,E,S,C),[n,E]),A=(0,U.useCallback)(e=>{t({search:ae(e,E,S,C),replace:!0})},[t,E]);async function j(){c(!0);try{await ze(h)}catch(e){h(`Error: ${e instanceof Error?e.message:`Unknown`}`)}finally{c(!1)}}(0,U.useEffect)(()=>{$||($=!0,j())},[]);let M=(0,U.useCallback)(e=>(0,W.jsxs)(x,{repo:e,children:[(0,W.jsx)(x.AddToList,{repo:e,lists:i??[],listItems:o??[]}),(0,W.jsx)(x.Rate,{repo:e}),(0,W.jsx)(x.Separator,{}),(0,W.jsx)(x.OpenOnGithub,{url:e.url}),(0,W.jsx)(x.CopyUrl,{url:e.url}),(0,W.jsx)(x.Separator,{}),(0,W.jsx)(x.Unstar,{repo:e})]}),[i,o]),N=(0,U.useMemo)(()=>fe({selectable:!0,onPreview:e=>_(e),renderActions:M}),[M]);return(0,W.jsxs)(`div`,{className:`space-y-6`,children:[(0,W.jsxs)(`div`,{className:`flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between`,children:[(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`h1`,{className:`font-display text-2xl font-bold tracking-tight sm:text-3xl`,children:`All Stars`}),(0,W.jsxs)(`p`,{className:`text-sm text-muted-foreground`,children:[b.length,` starred repositories`]})]}),u?(0,W.jsx)(we,{}):(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-3`,children:[m&&(0,W.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:m}),(0,W.jsxs)(ee,{children:[(0,W.jsxs)(ne,{render:(0,W.jsx)(f,{disabled:s}),children:[(0,W.jsx)(me,{className:`mr-2 h-4 w-4`}),s?`Importing...`:`Import Stars`,(0,W.jsx)(ye,{className:`ml-1 h-4 w-4`})]}),(0,W.jsxs)(pe,{align:`end`,className:`w-auto min-w-48`,children:[(0,W.jsx)(D,{onClick:j,children:`From GitHub`}),(0,W.jsx)(D,{disabled:b.length===0,onClick:()=>d(!0),children:`From an Astral export`})]})]}),l&&(0,W.jsx)(Pe,{stored:{repos:b,lists:i??[],list_items:o??[]},onClose:()=>d(!1)})]})]}),(0,W.jsx)(w,{items:b,registry:E,sortDefinitions:O,defaultSort:S,defaultGroupBy:C,viewState:k,onViewStateChange:A,searchFields:oe,children:(0,W.jsx)(se,{columns:N,getRowId:e=>e.id,toolbar:(0,W.jsx)(re,{}),onRowClick:y?void 0:_,renderMobileRow:(e,t)=>(0,W.jsx)(te,{repo:e,state:t,actions:M(e)}),renderBulkActions:(e,t)=>(0,W.jsx)(le,{selectedRepos:e,lists:i??[],listItems:o??[],clearSelection:t})})}),(0,W.jsx)(ue,{repo:g,lists:i??[],listItems:o??[],open:g!==null,onOpenChange:e=>{e||_(null)}})]})}export{Be as component};