Skip to content

Commit a2a5f45

Browse files
Stefan ErnstStefan Ernst
authored andcommitted
Bugfix for SSRF angle in AI connections, component consolidation
1 parent 80263da commit a2a5f45

61 files changed

Lines changed: 701 additions & 816 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frontend/src/design-system/tokens/themes/dark.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ html[data-color-mode="dark"] {
1515
--ds-surface-overlay: #282e33;
1616
--ds-surface-selected: #09326c;
1717
--ds-surface-card: #313940;
18+
--ds-surface-raised-hovered: #313940;
19+
20+
/* =================================================================
21+
SHADOW TOKENS
22+
================================================================= */
23+
--ds-shadow-raised: 0px 1px 1px rgba(0,0,0,0.5), 0px 0px 1px rgba(0,0,0,0.6);
1824

1925
/* =================================================================
2026
TEXT COLORS

frontend/src/design-system/tokens/themes/light.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ html[data-color-mode="light"] {
1717
--ds-surface-overlay: #ffffff;
1818
--ds-surface-selected: #e3f2fd;
1919
--ds-surface-card: #ffffff;
20+
--ds-surface-raised-hovered: #f5f6f7;
21+
22+
/* =================================================================
23+
SHADOW TOKENS
24+
================================================================= */
25+
--ds-shadow-raised: 0px 1px 1px #1E1F2140, 0px 0px 1px #1E1F214F;
2026

2127
/* =================================================================
2228
TEXT COLORS

frontend/src/design-system/viewer/pages/ItemKey.svelte

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
<h1 class="text-2xl font-bold mb-2" style="color: var(--ds-text);">ItemKey</h1>
77
<p class="mb-8" style="color: var(--ds-text-subtle);">
88
Standard display component for work item keys. Shows the workspace key with the item number in a consistent format.
9+
Styling is baked in: <code>text-xs font-mono</code> with <code>var(--ds-text-subtle)</code> color by default.
10+
Interactive variants (href or onClick) automatically add <code>hover:underline cursor-pointer</code>.
911
</p>
1012

1113
<!-- Basic Usage -->
@@ -43,42 +45,52 @@
4345
<section class="mb-10">
4446
<h2 class="text-lg font-semibold mb-4" style="color: var(--ds-text);">As Link</h2>
4547
<p class="mb-4 text-sm" style="color: var(--ds-text-subtle);">
46-
When <code>href</code> is provided, renders as a clickable link.
48+
When <code>href</code> is provided, renders as a clickable link with hover:underline.
4749
</p>
4850
<div class="flex items-center gap-4">
4951
<ItemKey
5052
item={{ workspace_item_number: 42 }}
5153
workspace={{ key: 'PROJ' }}
5254
href="#/workspace/PROJ/item/42"
53-
className="text-xs font-mono text-blue-600 hover:underline"
5455
/>
5556
<ItemKey
5657
item={{ workspace_item_number: 123 }}
5758
workspace={{ key: 'WIND' }}
5859
href="#/workspace/WIND/item/123"
59-
className="text-xs font-mono text-blue-600 hover:underline"
6060
/>
6161
</div>
6262
</section>
6363

64-
<!-- Custom Styling -->
64+
<!-- With onClick (Button) -->
6565
<section class="mb-10">
66-
<h2 class="text-lg font-semibold mb-4" style="color: var(--ds-text);">Custom Styling</h2>
67-
<div class="flex items-center gap-4">
68-
<ItemKey
69-
item={{ workspace_item_number: 42 }}
70-
workspace={{ key: 'PROJ' }}
71-
className="text-sm font-mono text-gray-900 font-semibold"
72-
/>
66+
<h2 class="text-lg font-semibold mb-4" style="color: var(--ds-text);">With onClick (Button)</h2>
67+
<p class="mb-4 text-sm" style="color: var(--ds-text-subtle);">
68+
When <code>onClick</code> is provided without <code>href</code>, renders as a <code>&lt;button&gt;</code> with hover:underline.
69+
</p>
70+
<ItemKey
71+
item={{ workspace_item_number: 42 }}
72+
workspace={{ key: 'PROJ' }}
73+
onClick={() => alert('Clicked PROJ-42')}
74+
/>
75+
</section>
76+
77+
<!-- Custom Style Override -->
78+
<section class="mb-10">
79+
<h2 class="text-lg font-semibold mb-4" style="color: var(--ds-text);">Custom Style Override</h2>
80+
<p class="mb-4 text-sm" style="color: var(--ds-text-subtle);">
81+
Use the <code>style</code> prop to override the default color (e.g. for gradient backgrounds).
82+
</p>
83+
<div class="flex items-center gap-4 p-4 rounded-lg" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
7384
<ItemKey
7485
item={{ workspace_item_number: 42 }}
7586
workspace={{ key: 'PROJ' }}
76-
className="text-xs font-mono bg-gray-100 px-2 py-1 rounded"
87+
style="color: rgba(255,255,255,0.7);"
7788
/>
7889
<ItemKey
79-
item={{ workspace_item_number: 42 }}
80-
workspace={{ key: 'PROJ' }}
81-
className="text-xs font-mono bg-blue-100 text-blue-800 px-2 py-1 rounded"
90+
item={{ workspace_item_number: 123 }}
91+
workspace={{ key: 'WIND' }}
92+
href="#"
93+
style="color: rgba(255,255,255,0.7);"
8294
/>
8395
</div>
8496
</section>
@@ -99,32 +111,13 @@
99111
<ItemKey
100112
item={{ workspace_item_number: item.number }}
101113
workspace={{ key: 'PROJ' }}
102-
className="text-xs font-mono text-gray-500"
103114
/>
104115
<span class="text-sm" style="color: var(--ds-text);">{item.title}</span>
105116
</div>
106117
{/each}
107118
</div>
108119
</section>
109120

110-
<!-- With Click Handler -->
111-
<section class="mb-10">
112-
<h2 class="text-lg font-semibold mb-4" style="color: var(--ds-text);">With Click Handler</h2>
113-
<p class="mb-4 text-sm" style="color: var(--ds-text-subtle);">
114-
Use <code>onClick</code> for custom behavior like opening a modal while preserving link benefits.
115-
</p>
116-
<ItemKey
117-
item={{ workspace_item_number: 42 }}
118-
workspace={{ key: 'PROJ' }}
119-
href="#/item/42"
120-
onClick={(e) => {
121-
e.preventDefault()
122-
alert('Would open item modal for PROJ-42')
123-
}}
124-
className="text-xs font-mono text-blue-600 hover:underline cursor-pointer"
125-
/>
126-
</section>
127-
128121
<!-- Props Reference -->
129122
<section class="mb-10">
130123
<h2 class="text-lg font-semibold mb-4" style="color: var(--ds-text);">Props Reference</h2>
@@ -162,9 +155,9 @@
162155
<td class="p-2">null</td>
163156
</tr>
164157
<tr>
165-
<td class="p-2"><code>className</code></td>
158+
<td class="p-2"><code>style</code></td>
166159
<td class="p-2">string</td>
167-
<td class="p-2">'text-xs font-mono text-gray-500'</td>
160+
<td class="p-2">"color: var(--ds-text-subtle);"</td>
168161
</tr>
169162
</tbody>
170163
</table>

frontend/src/lib/components/Card.svelte

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
let {
33
variant = 'raised', // 'raised', 'flat', 'outlined', 'dashed'
44
padding = 'default', // 'none', 'compact', 'default', 'spacious', 'loose', 'generous'
5-
shadow = false, // Add shadow-sm
6-
hoverable = false, // Add hover effects (shadow-md, translate-y)
5+
shadow = false, // Add raised shadow
6+
hoverable = false, // Add hover background effect
77
glass = false, // Glassmorphism effect
88
href = null, // If provided, renders as <a>
99
onclick = null, // Click handler
@@ -17,7 +17,7 @@
1717
1818
// Variant styles using design tokens
1919
const variantStyles = {
20-
raised: 'background-color: var(--ds-surface-raised); border-color: var(--ds-border);',
20+
raised: 'background-color: var(--ds-surface-raised); border-color: transparent; box-shadow: var(--ds-shadow-raised);',
2121
flat: 'background-color: var(--ds-surface); border-color: var(--ds-border);',
2222
outlined: 'background-color: transparent; border-color: var(--ds-border);',
2323
dashed: 'background-color: var(--ds-surface-raised); border-color: var(--ds-border); border-style: dashed;'
@@ -38,6 +38,7 @@
3838
// Rounded classes
3939
const roundedClasses = {
4040
none: '',
41+
xs: 'rounded-[3px]',
4142
sm: 'rounded-sm',
4243
md: 'rounded-md',
4344
lg: 'rounded-lg',
@@ -49,8 +50,7 @@
4950
const baseClasses = $derived([
5051
'border',
5152
roundedClasses[rounded],
52-
shadow ? 'shadow-sm' : '',
53-
hoverable ? 'transition-all duration-150 hover:shadow-md hover:-translate-y-px' : '',
53+
hoverable ? 'card-hoverable' : '',
5454
// When no header/footer, apply padding directly on outer element
5555
!hasStructure ? paddingClasses[padding] : '',
5656
className
@@ -60,6 +60,7 @@
6060
6161
const computedStyle = $derived([
6262
glass ? glassStyle : variantStyles[variant],
63+
shadow && variant !== 'raised' ? 'box-shadow: var(--ds-shadow-raised);' : '',
6364
userStyle
6465
].filter(Boolean).join(' '));
6566
</script>
@@ -113,4 +114,12 @@
113114
backdrop-filter: blur(12px) saturate(180%);
114115
-webkit-backdrop-filter: blur(12px) saturate(180%);
115116
}
117+
118+
.card-hoverable {
119+
transition: background-color 140ms ease-in-out, box-shadow 140ms ease-in-out;
120+
}
121+
122+
.card-hoverable:hover {
123+
background-color: var(--ds-surface-raised-hovered) !important;
124+
}
116125
</style>

frontend/src/lib/components/CardHeader.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<svelte:component this={icon} class="{iconSizeClasses[size]} flex-shrink-0" style="color: var(--ds-icon);" />
3030
{/if}
3131
<div class="min-w-0">
32-
<h3 class="{sizeClasses[size]} font-medium truncate" style="color: var(--ds-text);">
32+
<h3 class="{sizeClasses[size]} truncate" style="color: var(--ds-text);">
3333
{title}
3434
</h3>
3535
{#if subtitle}

frontend/src/lib/components/Lozenge.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
rounded = 'rounded', // 'rounded' | 'rounded-md'
1010
size = 'sm', // 'sm' | 'md'
1111
icon = null, // Optional Lucide icon component
12+
square = false, // Compact square indicator (no text)
1213
// Custom color props for dynamic colors (e.g., item type colors)
1314
customBg = null, // Hex color for background
1415
customBorder = null, // Hex color for border (defaults to customBg)
@@ -84,7 +85,7 @@
8485
</script>
8586

8687
<span
87-
class="inline-flex items-center gap-1 font-semibold border {rounded} {sizeClass}"
88+
class="inline-flex items-center {square ? '' : 'gap-1'} font-semibold border {rounded} {square ? 'w-4 h-4 flex-shrink-0' : sizeClass}"
8889
style={computedStyle}
8990
>
9091
{#if icon}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<script>
2+
import { fade } from 'svelte/transition';
3+
4+
let {
5+
show = $bindable(false),
6+
opacity = 0.5,
7+
blur = 2,
8+
extraFilter = '',
9+
zIndex = 50,
10+
align = 'center',
11+
paddingTop = '',
12+
scrollable = false,
13+
closeOnClick = true,
14+
closeOnEscape = true,
15+
transition = true,
16+
ariaLabelledBy = undefined,
17+
onclose = undefined,
18+
children,
19+
} = $props();
20+
21+
let backdropRef = $state(null);
22+
let previouslyFocusedElement = null;
23+
24+
const bgStyle = $derived(`rgba(0, 0, 0, ${opacity})`);
25+
const filterStyle = $derived(
26+
[blur > 0 ? `blur(${blur}px)` : '', extraFilter].filter(Boolean).join(' ') || 'none'
27+
);
28+
29+
const layoutClasses = $derived(
30+
align === 'center'
31+
? 'flex items-center justify-center p-4'
32+
: align === 'top'
33+
? `flex items-start justify-center ${paddingTop}${scrollable ? ' overflow-y-auto' : ''}`
34+
: ''
35+
);
36+
37+
// Focus management: save on open, restore on close
38+
$effect(() => {
39+
if (show && !previouslyFocusedElement) {
40+
previouslyFocusedElement = document.activeElement;
41+
}
42+
if (!show && previouslyFocusedElement) {
43+
previouslyFocusedElement?.focus();
44+
previouslyFocusedElement = null;
45+
}
46+
});
47+
48+
function handleIntroEnd() {
49+
backdropRef?.focus();
50+
}
51+
52+
function handleClick(event) {
53+
if (closeOnClick && event.target === event.currentTarget) {
54+
close();
55+
}
56+
}
57+
58+
function handleKeydown(event) {
59+
if (closeOnEscape && event.key === 'Escape') {
60+
close();
61+
}
62+
}
63+
64+
function close() {
65+
show = false;
66+
onclose?.();
67+
}
68+
</script>
69+
70+
{#if show}
71+
<div
72+
bind:this={backdropRef}
73+
transition:fade={{ duration: transition ? 150 : 0 }}
74+
onintroend={handleIntroEnd}
75+
class="fixed inset-0 {layoutClasses} focus:outline-none"
76+
style="z-index: {zIndex}; background-color: {bgStyle}; backdrop-filter: {filterStyle};"
77+
onclick={handleClick}
78+
onkeydown={handleKeydown}
79+
role="dialog"
80+
aria-modal="true"
81+
aria-labelledby={ariaLabelledBy}
82+
tabindex="-1"
83+
>
84+
{@render children?.()}
85+
</div>
86+
{/if}

frontend/src/lib/dialogs/AIConfirmModal.svelte

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<script>
22
import { X, Check, Square, CheckSquare } from 'lucide-svelte';
3-
import { fade, scale } from 'svelte/transition';
3+
import { scale } from 'svelte/transition';
44
import { backOut } from 'svelte/easing';
55
import Spinner from '../components/Spinner.svelte';
66
import Button from '../components/Button.svelte';
7+
import ModalBackdrop from '../components/ModalBackdrop.svelte';
78
89
let {
910
show = $bindable(false),
@@ -55,34 +56,11 @@
5556
oncreate?.(selectedTasks);
5657
}
5758
58-
function handleBackdropClick(e) {
59-
if (e.target === e.currentTarget) {
60-
close();
61-
}
62-
}
63-
64-
function handleKeydown(e) {
65-
if (e.key === 'Escape') {
66-
close();
67-
}
68-
}
69-
7059
let allSelected = $derived(selected.size === subTasks.length && subTasks.length > 0);
7160
let selectedCount = $derived(selected.size);
7261
</script>
7362
74-
{#if show}
75-
<div
76-
transition:fade={{ duration: 150 }}
77-
class="fixed inset-0 flex items-start justify-center pt-8 overflow-y-auto z-50"
78-
style="background-color: rgba(0, 0, 0, 0.4); backdrop-filter: blur(4px);"
79-
tabindex="-1"
80-
onclick={handleBackdropClick}
81-
onkeydown={handleKeydown}
82-
role="dialog"
83-
aria-modal="true"
84-
aria-labelledby="ai-confirm-title"
85-
>
63+
<ModalBackdrop bind:show opacity={0.4} blur={4} align="top" paddingTop="pt-8" scrollable onclose={close} ariaLabelledBy="ai-confirm-title">
8664
<div
8765
transition:scale={{ duration: 200, start: 0.95, easing: backOut }}
8866
class="relative rounded-xl overflow-hidden max-w-2xl w-full mx-4 mb-8"
@@ -209,5 +187,4 @@
209187
</div>
210188
{/if}
211189
</div>
212-
</div>
213-
{/if}
190+
</ModalBackdrop>

0 commit comments

Comments
 (0)