csr tangled client in solid-js
15

Configure Feed

Select the types of activity you want to include in your feed.

fix some visual inconsistencies

dawn (May 21, 2026, 4:14 AM +0300) e9d76155 faff020a

+188 -52
+7 -4
src/components/common.tsx
··· 25 25 type="button" 26 26 class={clsx( 27 27 'untangled-toggle-button inline-flex items-center gap-2 border-r border-gray-300 px-3 text-sm transition-colors last:border-r-0 dark:border-gray-700', 28 - props.active 29 - ? 'bg-gray-200 text-gray-900 dark:bg-gray-700 dark:text-gray-100' 30 - : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-300 dark:hover:bg-gray-700/50 dark:hover:text-gray-100', 28 + props.active ? 'untangled-toggle-button-active' : 'untangled-toggle-button-idle', 31 29 )} 32 30 onClick={props.onClick} 33 31 > 34 32 <span>{props.label}</span> 35 33 <Show when={props.meta}> 36 - <span class={clsx('text-xs', props.active ? 'text-gray-500 dark:text-gray-400' : 'text-gray-400 dark:text-gray-500')}> 34 + <span 35 + class={clsx( 36 + 'untangled-toggle-button-meta text-xs', 37 + props.active ? 'untangled-toggle-button-meta-active' : 'untangled-toggle-button-meta-idle', 38 + )} 39 + > 37 40 {props.meta} 38 41 </span> 39 42 </Show>
+46
src/components/repo.tsx
··· 5 5 ChevronRight, 6 6 Circle, 7 7 Columns2, 8 + Copy, 9 + ExternalLink, 8 10 FileText, 9 11 FoldVertical, 10 12 Folder, ··· 1112 1114 {props.name} 1113 1115 </span> 1114 1116 ); 1117 + 1118 + export const AtUriPanel: Component<{ uri: string }> = (props) => { 1119 + const selectUri: JSX.EventHandlerUnion<HTMLSpanElement, MouseEvent> = (event) => { 1120 + const selection = window.getSelection(); 1121 + if (!selection) return; 1122 + 1123 + const range = document.createRange(); 1124 + range.selectNodeContents(event.currentTarget); 1125 + selection.removeAllRanges(); 1126 + selection.addRange(range); 1127 + }; 1128 + 1129 + return ( 1130 + <section class="untangled-at-uri-panel"> 1131 + <div class="untangled-at-uri-header"> 1132 + <span>AT URI</span> 1133 + <div class="untangled-at-uri-actions"> 1134 + <button 1135 + type="button" 1136 + class="untangled-at-uri-action" 1137 + aria-label="copy AT URI" 1138 + title="Copy AT URI" 1139 + onClick={() => void navigator.clipboard?.writeText(props.uri)} 1140 + > 1141 + <Copy class="size-4" /> 1142 + </button> 1143 + <a 1144 + href={`https://pdsls.dev/${props.uri}`} 1145 + target="_blank" 1146 + rel="noreferrer" 1147 + class="untangled-at-uri-action" 1148 + aria-label="view AT URI in PDSls" 1149 + title="View in PDSls" 1150 + > 1151 + <ExternalLink class="size-4" /> 1152 + </a> 1153 + </div> 1154 + </div> 1155 + <span class="untangled-at-uri" title={props.uri} onClick={selectUri}> 1156 + {props.uri} 1157 + </span> 1158 + </section> 1159 + ); 1160 + };
+127 -11
src/index.css
··· 585 585 .untangled-pr-summary-card { 586 586 padding: 1rem; 587 587 } 588 + 589 + .untangled-thread-meta { 590 + padding: 0.25rem 0.25rem 0.5rem; 591 + } 588 592 } 589 593 590 594 html, ··· 748 752 border: 1px solid rgb(17 24 39); 749 753 } 750 754 755 + .untangled-at-uri-panel { 756 + min-width: 0; 757 + } 758 + 759 + .untangled-at-uri-header { 760 + display: flex; 761 + align-items: center; 762 + justify-content: space-between; 763 + gap: 0.5rem; 764 + margin-bottom: 0.5rem; 765 + color: rgb(107 114 128); 766 + font-size: 0.875rem; 767 + font-weight: 600; 768 + text-transform: uppercase; 769 + } 770 + 771 + .untangled-at-uri-actions { 772 + display: flex; 773 + align-items: center; 774 + gap: 0.5rem; 775 + } 776 + 777 + .untangled-at-uri-action { 778 + display: inline-flex; 779 + align-items: center; 780 + justify-content: center; 781 + border: 0; 782 + background: transparent; 783 + padding: 0; 784 + color: rgb(107 114 128); 785 + text-decoration: none; 786 + } 787 + 788 + .untangled-at-uri-action:hover { 789 + color: rgb(55 65 81); 790 + text-decoration: none; 791 + } 792 + 751 793 .untangled-at-uri { 752 - overflow-wrap: anywhere; 794 + display: block; 795 + max-width: 100%; 796 + overflow-x: auto; 797 + white-space: nowrap; 798 + color: rgb(55 65 81); 799 + cursor: pointer; 800 + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; 801 + font-size: 0.875rem; 802 + user-select: all; 803 + } 804 + 805 + @media (prefers-color-scheme: dark) { 806 + .untangled-at-uri-header, 807 + .untangled-at-uri-action { 808 + color: rgb(156 163 175); 809 + } 810 + 811 + .untangled-at-uri-action:hover { 812 + color: rgb(229 231 235); 813 + } 814 + 815 + .untangled-at-uri { 816 + color: rgb(209 213 219); 817 + } 753 818 } 754 819 755 820 ··· 902 967 min-height: 34px; 903 968 } 904 969 970 + .untangled-toggle-button { 971 + background: transparent; 972 + color: rgb(75 85 99); 973 + } 974 + 975 + .untangled-toggle-button-idle:hover { 976 + background: rgb(249 250 251); 977 + color: rgb(17 24 39); 978 + } 979 + 980 + .untangled-toggle-button-active { 981 + background: rgb(229 231 235); 982 + color: rgb(17 24 39); 983 + } 984 + 985 + .untangled-toggle-button-active:hover { 986 + background: rgb(243 244 246); 987 + color: rgb(17 24 39); 988 + } 989 + 990 + .untangled-toggle-button-meta-idle { 991 + color: rgb(156 163 175); 992 + } 993 + 994 + .untangled-toggle-button-meta-active, 995 + .untangled-toggle-button-idle:hover .untangled-toggle-button-meta { 996 + color: rgb(107 114 128); 997 + } 998 + 999 + @media (prefers-color-scheme: dark) { 1000 + .untangled-toggle-button { 1001 + color: rgb(209 213 219); 1002 + } 1003 + 1004 + .untangled-toggle-button-idle:hover { 1005 + background: rgb(55 65 81 / 0.5); 1006 + color: rgb(243 244 246); 1007 + } 1008 + 1009 + .untangled-toggle-button-active { 1010 + background: rgb(55 65 81); 1011 + color: rgb(243 244 246); 1012 + } 1013 + 1014 + .untangled-toggle-button-active:hover { 1015 + background: rgb(75 85 99); 1016 + color: rgb(243 244 246); 1017 + } 1018 + 1019 + .untangled-toggle-button-meta-idle { 1020 + color: rgb(107 114 128); 1021 + } 1022 + 1023 + .untangled-toggle-button-meta-active, 1024 + .untangled-toggle-button-idle:hover .untangled-toggle-button-meta { 1025 + color: rgb(156 163 175); 1026 + } 1027 + } 1028 + 905 1029 .untangled-button { 906 1030 min-height: 30px; 907 1031 } ··· 980 1104 981 1105 .untangled-file-row { 982 1106 display: grid; 983 - grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); 1107 + grid-template-columns: minmax(0, 1fr) max-content; 984 1108 align-items: center; 985 1109 gap: 1rem; 986 1110 padding: 0.25rem 0.5rem 0.25rem 0; ··· 995 1119 996 1120 .untangled-file-row-time { 997 1121 text-align: right; 1122 + white-space: nowrap; 998 1123 } 999 1124 1000 1125 .untangled-filter-grid { ··· 1886 2011 1887 2012 .untangled-repo-filter-search { 1888 2013 grid-row: 1; 1889 - } 1890 - 1891 - .untangled-file-row { 1892 - grid-template-columns: minmax(0, 1fr); 1893 - gap: 0.125rem; 1894 - } 1895 - 1896 - .untangled-file-row-time { 1897 - text-align: left; 1898 2014 } 1899 2015 1900 2016 .untangled-file-tree-pane {
+1
src/pages/repo/code.tsx
··· 901 901 <span class="font-medium text-gray-700 dark:text-gray-200"> 902 902 {commit().author?.name || 'unknown author'} 903 903 </span> 904 + <span class="select-none text-gray-400 dark:text-gray-500">·</span> 904 905 <span class="truncate text-gray-900 dark:text-gray-100">{commit().message.trim().split('\n')[0]}</span> 905 906 <span class="select-none text-gray-400 dark:text-gray-500">·</span> 906 907 <span class="text-gray-400 dark:text-gray-500">{formatRelativeTime(commit().when)}</span>
+4 -21
src/pages/repo/issues.tsx
··· 1 1 import clsx from 'clsx'; 2 - import { Ban, CirclePlus, Copy, ExternalLink, LoaderCircle, MessageSquarePlus, Pencil, Reply, Search, Smile, X } from 'lucide-solid'; 2 + import { Ban, CirclePlus, LoaderCircle, MessageSquarePlus, Pencil, Reply, Search, Smile, X } from 'lucide-solid'; 3 3 import { A, useNavigate, useParams, useSearchParams } from '@solidjs/router'; 4 4 import { createQuery, useQueryClient } from '@tanstack/solid-query'; 5 5 import type { ResourceUri } from '@atcute/lexicons/syntax'; ··· 7 7 import { createIssue, createIssueComment, getIssue, listIssuesPage, parseAtUri, resolveActor, setIssueState, type RepoContext } from '../../lib/api'; 8 8 import { useAuth } from '../../lib/auth'; 9 9 import { Avatar, ErrorState, PaginationControls, StateBadge, ToggleButton, buttonStyles, cardStyles, inputStyles, textareaStyles } from '../../components/common'; 10 - import { MarkdownBlock, RepoListSkeleton, RepoThreadSkeleton } from '../../components/repo'; 10 + import { AtUriPanel, MarkdownBlock, RepoListSkeleton, RepoThreadSkeleton } from '../../components/repo'; 11 11 import { RepoFrame, issueQueryKey, issuesQueryKey, useRepoQuery } from './shared'; 12 12 import { REPO_LIST_PAGE_LIMIT, formatRelativeTime, getErrorMessage, issueHref, parseIntegerSearchParam, uniqueCommenters } from '../../lib/repo-utils'; 13 13 import { buildIssueCommentThreads, buildIssueFilterQuery, parseIssueFilter } from './issues-helpers'; ··· 330 330 } 331 331 }; 332 332 333 - const copyIssueUri = async (uri: string) => { 334 - await navigator.clipboard?.writeText(uri).catch(() => undefined); 335 - }; 336 - 337 333 const submitReply = async (event: SubmitEvent, replyTo: ResourceUri) => { 338 334 event.preventDefault(); 339 335 const agent = auth.agent(); ··· 566 562 </div> 567 563 </div> 568 564 569 - <div class="col-span-1 flex min-w-0 flex-col gap-6 text-sm md:col-span-2"> 565 + <div class="untangled-thread-meta col-span-1 flex min-w-0 flex-col gap-6 text-sm md:col-span-2"> 570 566 <section> 571 567 <div class="flex items-center justify-between gap-2 font-semibold uppercase text-gray-500 dark:text-gray-400"> 572 568 <span>Labels</span> ··· 591 587 </For> 592 588 </div> 593 589 </section> 594 - <section> 595 - <div class="mb-2 flex items-center justify-between gap-2 font-semibold uppercase text-gray-500 dark:text-gray-400"> 596 - <span>AT URI</span> 597 - <div class="flex items-center gap-2"> 598 - <button type="button" class="text-gray-400 hover:text-gray-200" onClick={() => void copyIssueUri(detail().issue.uri)}> 599 - <Copy class="size-4" /> 600 - </button> 601 - <a href={`https://tangled.org/${repoQuery.data!.owner.handle}/${repoQuery.data!.slug}/issues/${detail().issue.number}`} target="_blank" rel="noreferrer" class="text-gray-400 hover:text-gray-200"> 602 - <ExternalLink class="size-4" /> 603 - </a> 604 - </div> 605 - </div> 606 - <div class="untangled-at-uri font-mono text-xs text-gray-100">{detail().issue.uri}</div> 607 - </section> 590 + <AtUriPanel uri={detail().issue.uri} /> 608 591 </div> 609 592 </div> 610 593 );
+3 -16
src/pages/repo/pulls.tsx
··· 20 20 import { compareBranches, createPull, createPullComment, fetchPullRoundPatch, getPull, getRepoBranches, listPullsPage, parseAtUri, resolveActor, setPullStatus, type RepoContext } from '../../lib/api'; 21 21 import { useAuth } from '../../lib/auth'; 22 22 import { Avatar, ErrorState, LoadingState, PaginationControls, StateBadge, ToggleButton, buttonStyles, cardStyles, textareaStyles } from '../../components/common'; 23 - import { BranchPill, CommentCard, DiffView, MarkdownBlock, PullDiffSkeleton, PullDiffView, RepoListSkeleton, RepoThreadSkeleton } from '../../components/repo'; 23 + import { AtUriPanel, BranchPill, CommentCard, DiffView, MarkdownBlock, PullDiffSkeleton, PullDiffView, RepoListSkeleton, RepoThreadSkeleton } from '../../components/repo'; 24 24 import { RepoFrame, pullQueryKey, pullsQueryKey, useRepoQuery } from './shared'; 25 25 import { REPO_LIST_PAGE_LIMIT, formatRelativeTime, getErrorMessage, parseIntegerSearchParam, pullHref, uniqueCommenters } from '../../lib/repo-utils'; 26 26 import { buildPullFilterQuery, parsePastedPatchPrefill, parsePullFilter, type PullSourceMode } from './pulls-helpers'; ··· 916 916 </div> 917 917 </section> 918 918 919 - <aside class="untangled-pr-meta"> 919 + <aside class="untangled-pr-meta untangled-thread-meta"> 920 920 <div class="untangled-pr-meta-section"> 921 921 <h2>Labels</h2> 922 922 <p>None yet.</p> ··· 935 935 </For> 936 936 </div> 937 937 </div> 938 - <div class="untangled-pr-meta-section"> 939 - <h2>AT URI</h2> 940 - <div class="flex min-w-0 items-center gap-2"> 941 - <code class="truncate">{detail().pull.uri}</code> 942 - <button 943 - type="button" 944 - class="untangled-pr-meta-action" 945 - aria-label="copy AT URI" 946 - onClick={() => void navigator.clipboard?.writeText(detail().pull.uri)} 947 - > 948 - <Copy class="size-4" /> 949 - </button> 950 - </div> 951 - </div> 938 + <AtUriPanel uri={detail().pull.uri} /> 952 939 </aside> 953 940 </div> 954 941