+
Sidebar filter
)}
@@ -635,15 +697,16 @@ export default function ViewsOverlay({
}
}}
disabled={!!externalDimensionFilter}
+ title="Filter (⌘F)"
style={{
display: 'flex',
alignItems: 'center',
gap: '4px',
padding: '4px 8px',
background: 'transparent',
- border: '1px solid var(--rah-border-strong)',
+ border: '1px solid var(--rah-border)',
borderRadius: '5px',
- color: externalDimensionFilter ? '#4b4b4b' : '#888',
+ color: externalDimensionFilter ? 'var(--rah-text-muted)' : 'var(--rah-text-soft)',
fontSize: '11px',
cursor: externalDimensionFilter ? 'not-allowed' : 'pointer',
transition: 'all 0.15s ease'
@@ -651,12 +714,12 @@ export default function ViewsOverlay({
onMouseEnter={(e) => {
if (!externalDimensionFilter) {
e.currentTarget.style.background = 'rgba(255,255,255,0.04)';
- e.currentTarget.style.borderColor = '#333';
+ e.currentTarget.style.borderColor = 'var(--rah-border-strong)';
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'transparent';
- e.currentTarget.style.borderColor = '#222';
+ e.currentTarget.style.borderColor = 'var(--rah-border)';
}}
>
@@ -670,8 +733,8 @@ export default function ViewsOverlay({
top: '100%',
left: 0,
marginTop: '4px',
- background: 'var(var(--rah-bg-panel))',
- border: '1px solid var(--rah-border-strong)',
+ background: 'var(--rah-bg-panel)',
+ border: '1px solid var(--rah-border)',
borderRadius: '10px',
padding: '6px',
minWidth: '220px',
@@ -689,23 +752,23 @@ export default function ViewsOverlay({
style={{
width: '100%',
padding: '7px 10px',
- background: 'var(var(--rah-bg-surface))',
+ background: 'var(--rah-bg-base)',
border: '1px solid transparent',
borderRadius: '6px',
- color: 'var(var(--rah-text-active))',
+ color: 'var(--rah-text-active)',
fontSize: '12px',
marginBottom: '4px',
outline: 'none',
}}
- onFocus={(e) => { e.currentTarget.style.borderColor = '#333'; }}
+ onFocus={(e) => { e.currentTarget.style.borderColor = 'var(--rah-border-strong)'; }}
onBlur={(e) => { e.currentTarget.style.borderColor = 'transparent'; }}
/>
{dimensionsLoading ? (
-
+
Loading dimensions...
) : filterPickerDimensions.length === 0 ? (
-
+
{filterSearchQuery ? 'No matching dimensions' : 'No dimensions available'}
) : (
@@ -722,7 +785,7 @@ export default function ViewsOverlay({
background: 'transparent',
border: 'none',
borderRadius: '5px',
- color: 'var(var(--rah-text-secondary))',
+ color: 'var(--rah-text-secondary)',
fontSize: '12px',
cursor: 'pointer',
textAlign: 'left'
@@ -732,9 +795,9 @@ export default function ViewsOverlay({
>
{d.dimension}
@@ -754,12 +817,12 @@ export default function ViewsOverlay({
padding: '4px 8px',
background: 'transparent',
border: 'none',
- color: 'var(var(--rah-text-muted))',
+ color: 'var(--rah-text-muted)',
fontSize: '11px',
cursor: 'pointer'
}}
onMouseEnter={(e) => { e.currentTarget.style.color = '#ef4444'; }}
- onMouseLeave={(e) => { e.currentTarget.style.color = '#666'; }}
+ onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--rah-text-muted)'; }}
>
Clear all
@@ -768,29 +831,29 @@ export default function ViewsOverlay({
{/* Sort dropdown */}
-
setShowSortDropdown(!showSortDropdown)}
- style={{
- display: 'flex',
- alignItems: 'center',
- gap: '4px',
- padding: '5px 8px',
- background: 'transparent',
- border: '1px solid var(--rah-border-strong)',
- borderRadius: '5px',
- color: 'var(var(--rah-text-muted))',
- fontSize: '11px',
- cursor: 'pointer',
- transition: 'all 0.15s ease',
+ setShowSortDropdown(!showSortDropdown)}
+ style={{
+ display: 'flex',
+ alignItems: 'center',
+ gap: '4px',
+ padding: '5px 8px',
+ background: 'transparent',
+ border: '1px solid var(--rah-border)',
+ borderRadius: '5px',
+ color: 'var(--rah-text-soft)',
+ fontSize: '11px',
+ cursor: 'pointer',
+ transition: 'all 0.15s ease',
whiteSpace: 'nowrap',
}}
onMouseEnter={(e) => {
e.currentTarget.style.background = 'rgba(255,255,255,0.04)';
- e.currentTarget.style.borderColor = '#333';
+ e.currentTarget.style.borderColor = 'var(--rah-border-strong)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.background = 'transparent';
- e.currentTarget.style.borderColor = '#222';
+ e.currentTarget.style.borderColor = 'var(--rah-border)';
}}
>
@@ -804,8 +867,8 @@ export default function ViewsOverlay({
top: '100%',
right: 0,
marginTop: '4px',
- background: 'var(var(--rah-bg-panel))',
- border: '1px solid var(--rah-border-strong)',
+ background: 'var(--rah-bg-panel)',
+ border: '1px solid var(--rah-border)',
borderRadius: '10px',
padding: '4px',
minWidth: '140px',
@@ -828,7 +891,7 @@ export default function ViewsOverlay({
background: sortOrder === key ? 'rgba(255,255,255,0.04)' : 'transparent',
border: 'none',
borderRadius: '5px',
- color: sortOrder === key ? '#f0f0f0' : '#999',
+ color: sortOrder === key ? 'var(--rah-text-active)' : 'var(--rah-text-soft)',
fontSize: '12px',
cursor: 'pointer',
textAlign: 'left',
@@ -836,7 +899,7 @@ export default function ViewsOverlay({
onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(255,255,255,0.04)'; }}
onMouseLeave={(e) => { e.currentTarget.style.background = sortOrder === key ? 'rgba(255,255,255,0.04)' : 'transparent'; }}
>
- {sortOrder === key && ✓ }
+ {sortOrder === key && ✓ }
{SORT_LABELS[key]}
))}
@@ -844,6 +907,7 @@ export default function ViewsOverlay({
)}
+
);
return (
@@ -868,30 +932,55 @@ export default function ViewsOverlay({
{/* Content area — list view */}
{filteredNodesLoading ? (
-
- Loading...
+
+
+ {Array.from({ length: 6 }, (_, index) => (
+
+ ))}
+
) : filteredNodes.length === 0 ? (
-
- {selectedFilters.length > 0 ? 'No nodes match the selected filters.' : 'No nodes yet. Add some content to get started.'}
+
+
+
+
+ {selectedFilters.length > 0 ? 'Nothing matches these filters' : 'Nothing here yet'}
+
+
+ {selectedFilters.length > 0 ? 'Try adjusting your filters, or add a node with ⌘N' : 'Add a node with ⌘N to get started'}
+
+
) : (
- {pendingNodes && pendingNodes.length > 0 && pendingNodes.map(p => (
-
onDismissPending(p.id) : undefined}
- />
- ))}
- {filteredNodes.map((node, index) => renderNodeCard(node, index))}
+
+ {pendingNodes && pendingNodes.length > 0 && pendingNodes.map(p => (
+
onDismissPending(p.id) : undefined}
+ />
+ ))}
+ {filteredNodes.map((node, index) => renderNodeCard(node, index))}
+
)}
diff --git a/src/utils/formatDate.ts b/src/utils/formatDate.ts
new file mode 100644
index 0000000..f06221d
--- /dev/null
+++ b/src/utils/formatDate.ts
@@ -0,0 +1,24 @@
+export function formatRelativeDate(dateString?: string | null): string {
+ if (!dateString) return '';
+
+ const then = new Date(dateString).getTime();
+ if (Number.isNaN(then)) return '';
+
+ const diff = Date.now() - then;
+ if (diff < 0) return 'today';
+
+ const minutes = Math.floor(diff / 60000);
+ if (minutes < 1) return 'just now';
+ if (minutes < 60) return `${minutes}m ago`;
+
+ const hours = Math.floor(minutes / 60);
+ if (hours < 24) return `${hours}h ago`;
+
+ const days = Math.floor(hours / 24);
+ if (days === 1) return 'yesterday';
+ if (days < 7) return `${days}d ago`;
+ if (days < 30) return `${Math.floor(days / 7)}w ago`;
+ if (days < 365) return `${Math.floor(days / 30)}mo ago`;
+
+ return `${Math.floor(days / 365)}y ago`;
+}