@flowdrop/flowdrop 1.12.0 → 1.13.0
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/dist/components/ConfigForm.svelte +1 -0
- package/dist/components/SchemaForm.svelte +1 -0
- package/dist/components/form/FormAutocomplete.svelte +67 -10
- package/dist/components/form/FormField.svelte +21 -0
- package/dist/components/form/FormFieldLight.svelte +1 -0
- package/dist/components/interrupt/InterruptBubble.svelte +76 -17
- package/dist/components/interrupt/InterruptBubble.svelte.d.ts +11 -0
- package/dist/components/playground/ChatBubble.svelte +289 -0
- package/dist/components/playground/ChatBubble.svelte.d.ts +10 -0
- package/dist/components/playground/HierarchyTrail.svelte +88 -0
- package/dist/components/playground/HierarchyTrail.svelte.d.ts +7 -0
- package/dist/components/playground/LogRow.svelte +178 -0
- package/dist/components/playground/LogRow.svelte.d.ts +8 -0
- package/dist/components/playground/MessageBubble.stories.svelte +89 -0
- package/dist/components/playground/MessageBubble.svelte +25 -737
- package/dist/components/playground/MessageBubble.svelte.d.ts +3 -11
- package/dist/components/playground/MessageCard.svelte +106 -0
- package/dist/components/playground/MessageCard.svelte.d.ts +10 -0
- package/dist/components/playground/MessageMarkdown.svelte +160 -0
- package/dist/components/playground/MessageMarkdown.svelte.d.ts +7 -0
- package/dist/components/playground/MessageNotice.svelte +120 -0
- package/dist/components/playground/MessageNotice.svelte.d.ts +9 -0
- package/dist/components/playground/MessageStream.svelte +85 -1
- package/dist/components/playground/MessageTagChip.svelte +99 -0
- package/dist/components/playground/MessageTagChip.svelte.d.ts +7 -0
- package/dist/components/playground/MessageTagStrip.svelte +37 -0
- package/dist/components/playground/MessageTagStrip.svelte.d.ts +7 -0
- package/dist/components/playground/PlaygroundStudio.svelte +78 -0
- package/dist/components/playground/messageDisplay.d.ts +19 -0
- package/dist/components/playground/messageDisplay.js +62 -0
- package/dist/form/autocomplete.d.ts +1 -0
- package/dist/form/autocomplete.js +1 -0
- package/dist/form/index.d.ts +17 -0
- package/dist/form/index.js +19 -0
- package/dist/messages/defaults.d.ts +5 -0
- package/dist/messages/defaults.js +6 -0
- package/dist/schemas/v1/workflow.schema.json +10 -1
- package/dist/services/categoriesApi.d.ts +2 -1
- package/dist/services/categoriesApi.js +5 -3
- package/dist/services/portConfigApi.d.ts +2 -1
- package/dist/services/portConfigApi.js +5 -3
- package/dist/svelte-app.d.ts +1 -0
- package/dist/svelte-app.js +5 -5
- package/dist/types/index.d.ts +13 -0
- package/dist/types/playground.d.ts +76 -0
- package/dist/types/playground.js +14 -0
- package/package.json +6 -1
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
import type
|
|
2
|
-
/**
|
|
3
|
-
* Component props
|
|
4
|
-
*/
|
|
1
|
+
import { type PlaygroundMessage } from '../../types/playground.js';
|
|
5
2
|
interface Props {
|
|
6
|
-
/** The message to display */
|
|
7
3
|
message: PlaygroundMessage;
|
|
8
|
-
/** Whether to show the timestamp */
|
|
9
4
|
showTimestamp?: boolean;
|
|
10
|
-
/** Whether this is the last message (affects styling) */
|
|
11
5
|
isLast?: boolean;
|
|
12
|
-
/** Whether to render markdown content */
|
|
13
6
|
enableMarkdown?: boolean;
|
|
14
7
|
/**
|
|
15
8
|
* Use compact display mode for system messages.
|
|
16
|
-
* When true, system messages
|
|
17
|
-
*
|
|
18
|
-
* @default true
|
|
9
|
+
* When true (default), system messages without an explicit `display`
|
|
10
|
+
* default to the 'notice' layout instead of 'bubble'.
|
|
19
11
|
*/
|
|
20
12
|
compactSystemMessages?: boolean;
|
|
21
13
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MessageCard — vertical card layout: hierarchy (top) · body (middle) · tags (bottom).
|
|
3
|
+
Picks up shared markdown typography via <MessageMarkdown>.
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import type { PlaygroundMessage } from '../../types/playground.js';
|
|
8
|
+
import HierarchyTrail from './HierarchyTrail.svelte';
|
|
9
|
+
import MessageTagStrip from './MessageTagStrip.svelte';
|
|
10
|
+
import MessageMarkdown from './MessageMarkdown.svelte';
|
|
11
|
+
import { formatTimestamp, getRoleLabel } from './messageDisplay.js';
|
|
12
|
+
import { m } from '../../messages/index.js';
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
message: PlaygroundMessage;
|
|
16
|
+
showTimestamp?: boolean;
|
|
17
|
+
isLast?: boolean;
|
|
18
|
+
enableMarkdown?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { message, showTimestamp = true, isLast = false, enableMarkdown = true }: Props = $props();
|
|
22
|
+
|
|
23
|
+
const level = $derived(message.metadata?.level);
|
|
24
|
+
const hierarchy = $derived(message.hierarchy ?? []);
|
|
25
|
+
const tags = $derived(message.tags ?? []);
|
|
26
|
+
const roleLabel = $derived(getRoleLabel(message, m().playground.roles));
|
|
27
|
+
// Logs render as plain text; everything else respects enableMarkdown.
|
|
28
|
+
const markdown = $derived(enableMarkdown && message.role !== 'log');
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<article
|
|
32
|
+
class="message-card"
|
|
33
|
+
class:message-card--last={isLast}
|
|
34
|
+
class:message-card--error={level === 'error'}
|
|
35
|
+
class:message-card--warning={level === 'warning'}
|
|
36
|
+
aria-label={roleLabel}
|
|
37
|
+
>
|
|
38
|
+
{#if hierarchy.length > 0 || showTimestamp}
|
|
39
|
+
<header class="message-card__header">
|
|
40
|
+
<HierarchyTrail items={hierarchy} />
|
|
41
|
+
{#if showTimestamp}
|
|
42
|
+
<time
|
|
43
|
+
class="message-card__timestamp"
|
|
44
|
+
datetime={message.timestamp}
|
|
45
|
+
aria-label="sent at {formatTimestamp(message.timestamp)}"
|
|
46
|
+
>{formatTimestamp(message.timestamp)}</time>
|
|
47
|
+
{/if}
|
|
48
|
+
</header>
|
|
49
|
+
{/if}
|
|
50
|
+
<MessageMarkdown content={message.content} enableMarkdown={markdown} />
|
|
51
|
+
<MessageTagStrip {tags} />
|
|
52
|
+
</article>
|
|
53
|
+
|
|
54
|
+
<style>
|
|
55
|
+
.message-card {
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
gap: var(--fd-space-xs);
|
|
59
|
+
margin: var(--fd-space-3xs) var(--fd-space-xl);
|
|
60
|
+
padding: var(--fd-space-sm) var(--fd-space-md);
|
|
61
|
+
border-radius: var(--fd-radius-lg);
|
|
62
|
+
background-color: var(--fd-card);
|
|
63
|
+
border: 1px solid var(--fd-border);
|
|
64
|
+
color: var(--fd-card-foreground);
|
|
65
|
+
font-size: var(--fd-text-sm);
|
|
66
|
+
line-height: var(--fd-leading-normal);
|
|
67
|
+
/* fd-fade-in + reduced-motion guard live in MessageStream.svelte */
|
|
68
|
+
animation: fd-fade-in 0.18s ease-out;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.message-card--last {
|
|
72
|
+
margin-bottom: var(--fd-space-xl);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.message-card--error {
|
|
76
|
+
border-color: var(--fd-error);
|
|
77
|
+
background-color: color-mix(in srgb, var(--fd-error) 6%, var(--fd-card));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.message-card--warning {
|
|
81
|
+
border-color: var(--fd-warning);
|
|
82
|
+
background-color: color-mix(in srgb, var(--fd-warning) 6%, var(--fd-card));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.message-card__header {
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
justify-content: space-between;
|
|
89
|
+
gap: var(--fd-space-sm);
|
|
90
|
+
min-width: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.message-card__timestamp {
|
|
94
|
+
flex-shrink: 0;
|
|
95
|
+
font-size: 0.625rem;
|
|
96
|
+
font-family: var(--fd-font-mono);
|
|
97
|
+
color: var(--fd-muted-foreground);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@media (max-width: 640px) {
|
|
101
|
+
.message-card {
|
|
102
|
+
margin: var(--fd-space-3xs) var(--fd-space-md);
|
|
103
|
+
padding: var(--fd-space-xs) var(--fd-space-sm);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PlaygroundMessage } from '../../types/playground.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
message: PlaygroundMessage;
|
|
4
|
+
showTimestamp?: boolean;
|
|
5
|
+
isLast?: boolean;
|
|
6
|
+
enableMarkdown?: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare const MessageCard: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type MessageCard = ReturnType<typeof MessageCard>;
|
|
10
|
+
export default MessageCard;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MessageMarkdown Component
|
|
3
|
+
|
|
4
|
+
Renders message content. Wraps marked + sanitizeHtml and applies the
|
|
5
|
+
shared markdown typography. Used by ChatBubble (assistant/system/user)
|
|
6
|
+
and MessageCard so they share one set of typography rules.
|
|
7
|
+
|
|
8
|
+
Consumers scope overrides via :global(.message-markdown ...) from their
|
|
9
|
+
own component CSS — see ChatBubble's user-bubble rules.
|
|
10
|
+
|
|
11
|
+
When `enableMarkdown` is false (or the role is 'log'), the content is
|
|
12
|
+
rendered as plain text.
|
|
13
|
+
-->
|
|
14
|
+
|
|
15
|
+
<script lang="ts">
|
|
16
|
+
import { marked } from 'marked';
|
|
17
|
+
import { sanitizeHtml } from '../../utils/sanitize.js';
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
content: string;
|
|
21
|
+
enableMarkdown?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let { content, enableMarkdown = true }: Props = $props();
|
|
25
|
+
|
|
26
|
+
const rendered = $derived(
|
|
27
|
+
enableMarkdown ? sanitizeHtml(marked.parse(content || '') as string) : null
|
|
28
|
+
);
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<div class="message-markdown">
|
|
32
|
+
{#if rendered !== null}
|
|
33
|
+
<!-- Markdown sanitized via DOMPurify in sanitizeHtml. -->
|
|
34
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
35
|
+
{@html rendered}
|
|
36
|
+
{:else}
|
|
37
|
+
{content}
|
|
38
|
+
{/if}
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
.message-markdown {
|
|
43
|
+
line-height: var(--fd-leading-relaxed);
|
|
44
|
+
word-break: break-word;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.message-markdown :global(p) {
|
|
48
|
+
margin: 0 0 var(--fd-space-md) 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.message-markdown :global(p:last-child) {
|
|
52
|
+
margin-bottom: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.message-markdown :global(h1),
|
|
56
|
+
.message-markdown :global(h2),
|
|
57
|
+
.message-markdown :global(h3),
|
|
58
|
+
.message-markdown :global(h4),
|
|
59
|
+
.message-markdown :global(h5),
|
|
60
|
+
.message-markdown :global(h6) {
|
|
61
|
+
margin: var(--fd-space-xl) 0 var(--fd-space-xs) 0;
|
|
62
|
+
font-weight: 600;
|
|
63
|
+
line-height: 1.3;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.message-markdown :global(h1:first-child),
|
|
67
|
+
.message-markdown :global(h2:first-child),
|
|
68
|
+
.message-markdown :global(h3:first-child),
|
|
69
|
+
.message-markdown :global(h4:first-child),
|
|
70
|
+
.message-markdown :global(h5:first-child),
|
|
71
|
+
.message-markdown :global(h6:first-child) {
|
|
72
|
+
margin-top: 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.message-markdown :global(h1) { font-size: var(--fd-text-xl); }
|
|
76
|
+
.message-markdown :global(h2) { font-size: var(--fd-text-lg); }
|
|
77
|
+
.message-markdown :global(h3) { font-size: var(--fd-text-base); }
|
|
78
|
+
|
|
79
|
+
.message-markdown :global(ul),
|
|
80
|
+
.message-markdown :global(ol) {
|
|
81
|
+
margin: var(--fd-space-xs) 0;
|
|
82
|
+
padding-left: var(--fd-space-3xl);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.message-markdown :global(li) {
|
|
86
|
+
margin: var(--fd-space-3xs) 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.message-markdown :global(code) {
|
|
90
|
+
background-color: var(--fd-secondary);
|
|
91
|
+
padding: 0.125rem var(--fd-space-3xs);
|
|
92
|
+
border-radius: var(--fd-radius-sm);
|
|
93
|
+
font-family: var(--fd-font-mono);
|
|
94
|
+
font-size: 0.875em;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.message-markdown :global(pre) {
|
|
98
|
+
background-color: var(--fd-foreground);
|
|
99
|
+
color: var(--fd-background);
|
|
100
|
+
padding: var(--fd-space-md) var(--fd-space-xl);
|
|
101
|
+
border-radius: var(--fd-radius-lg);
|
|
102
|
+
overflow-x: auto;
|
|
103
|
+
margin: var(--fd-space-md) 0;
|
|
104
|
+
font-size: var(--fd-text-sm);
|
|
105
|
+
line-height: var(--fd-leading-normal);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.message-markdown :global(pre code) {
|
|
109
|
+
background-color: transparent;
|
|
110
|
+
padding: 0;
|
|
111
|
+
border-radius: 0;
|
|
112
|
+
color: inherit;
|
|
113
|
+
font-size: inherit;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.message-markdown :global(blockquote) {
|
|
117
|
+
border-left: 3px solid var(--fd-border-strong);
|
|
118
|
+
padding-left: var(--fd-space-xl);
|
|
119
|
+
margin: var(--fd-space-md) 0;
|
|
120
|
+
color: var(--fd-muted-foreground);
|
|
121
|
+
font-style: italic;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.message-markdown :global(a) {
|
|
125
|
+
color: var(--fd-primary);
|
|
126
|
+
text-decoration: none;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.message-markdown :global(a:hover) {
|
|
130
|
+
text-decoration: underline;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.message-markdown :global(hr) {
|
|
134
|
+
border: none;
|
|
135
|
+
border-top: 1px solid var(--fd-border);
|
|
136
|
+
margin: var(--fd-space-xl) 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.message-markdown :global(table) {
|
|
140
|
+
border-collapse: collapse;
|
|
141
|
+
width: 100%;
|
|
142
|
+
margin: var(--fd-space-md) 0;
|
|
143
|
+
font-size: var(--fd-text-sm);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.message-markdown :global(th),
|
|
147
|
+
.message-markdown :global(td) {
|
|
148
|
+
border: 1px solid var(--fd-border);
|
|
149
|
+
padding: var(--fd-space-xs) var(--fd-space-md);
|
|
150
|
+
text-align: left;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.message-markdown :global(th) {
|
|
154
|
+
background-color: var(--fd-muted);
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.message-markdown :global(strong) { font-weight: 600; }
|
|
159
|
+
.message-markdown :global(em) { font-style: italic; }
|
|
160
|
+
</style>
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MessageNotice — compact centered inline notice for system messages.
|
|
3
|
+
No role="status" — the parent <div role="log"> already announces additions.
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import Icon from '@iconify/svelte';
|
|
8
|
+
import type { PlaygroundMessage } from '../../types/playground.js';
|
|
9
|
+
import HierarchyTrail from './HierarchyTrail.svelte';
|
|
10
|
+
import MessageTagStrip from './MessageTagStrip.svelte';
|
|
11
|
+
import { formatTimestamp, getLogLevelIcon } from './messageDisplay.js';
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
message: PlaygroundMessage;
|
|
15
|
+
showTimestamp?: boolean;
|
|
16
|
+
isLast?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let { message, showTimestamp = true, isLast = false }: Props = $props();
|
|
20
|
+
|
|
21
|
+
const level = $derived(message.metadata?.level);
|
|
22
|
+
const hierarchy = $derived(message.hierarchy ?? []);
|
|
23
|
+
const tags = $derived(message.tags ?? []);
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<div
|
|
27
|
+
class="system-notice"
|
|
28
|
+
class:system-notice--last={isLast}
|
|
29
|
+
class:system-notice--warning={level === 'warning'}
|
|
30
|
+
class:system-notice--error={level === 'error'}
|
|
31
|
+
class:system-notice--debug={level === 'debug'}
|
|
32
|
+
>
|
|
33
|
+
<Icon icon={getLogLevelIcon(level)} class="system-notice__icon" aria-hidden="true" />
|
|
34
|
+
{#if message.metadata?.source}
|
|
35
|
+
<span class="system-notice__source">{message.metadata.source}</span>
|
|
36
|
+
{/if}
|
|
37
|
+
<HierarchyTrail items={hierarchy} />
|
|
38
|
+
<span class="system-notice__text">{message.content}</span>
|
|
39
|
+
<MessageTagStrip {tags} />
|
|
40
|
+
{#if showTimestamp}
|
|
41
|
+
<time
|
|
42
|
+
class="system-notice__timestamp"
|
|
43
|
+
datetime={message.timestamp}
|
|
44
|
+
aria-label="sent at {formatTimestamp(message.timestamp)}"
|
|
45
|
+
>{formatTimestamp(message.timestamp)}</time>
|
|
46
|
+
{/if}
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
.system-notice {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-wrap: wrap;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
gap: var(--fd-space-3xs);
|
|
56
|
+
padding: var(--fd-space-3xs) var(--fd-space-md);
|
|
57
|
+
margin: var(--fd-space-3xs) 0;
|
|
58
|
+
font-size: var(--fd-text-xs);
|
|
59
|
+
color: var(--fd-muted-foreground);
|
|
60
|
+
text-align: center;
|
|
61
|
+
min-width: 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.system-notice--last {
|
|
65
|
+
margin-bottom: var(--fd-space-md);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.system-notice :global(.system-notice__icon) {
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
font-size: var(--fd-text-sm);
|
|
71
|
+
color: var(--fd-border-strong);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.system-notice__source {
|
|
75
|
+
flex-shrink: 0;
|
|
76
|
+
font-size: 0.6rem;
|
|
77
|
+
text-transform: uppercase;
|
|
78
|
+
letter-spacing: 0.06em;
|
|
79
|
+
color: var(--fd-muted-foreground);
|
|
80
|
+
background-color: var(--fd-muted);
|
|
81
|
+
border: 1px solid var(--fd-border);
|
|
82
|
+
border-radius: var(--fd-radius-sm);
|
|
83
|
+
padding: 0 0.25rem;
|
|
84
|
+
line-height: 1.4;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.system-notice__text {
|
|
88
|
+
min-width: 0;
|
|
89
|
+
overflow-wrap: anywhere;
|
|
90
|
+
line-height: var(--fd-leading-tight);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.system-notice--warning,
|
|
94
|
+
.system-notice--warning :global(.system-notice__icon) {
|
|
95
|
+
color: var(--fd-warning);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.system-notice--error,
|
|
99
|
+
.system-notice--error :global(.system-notice__icon) {
|
|
100
|
+
color: var(--fd-error);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.system-notice--debug {
|
|
104
|
+
color: var(--fd-border-strong);
|
|
105
|
+
opacity: 0.6;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.system-notice__timestamp {
|
|
109
|
+
flex-shrink: 0;
|
|
110
|
+
font-size: 0.625rem;
|
|
111
|
+
color: var(--fd-border-strong);
|
|
112
|
+
font-family: var(--fd-font-mono);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@media (max-width: 640px) {
|
|
116
|
+
.system-notice__timestamp {
|
|
117
|
+
display: none;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PlaygroundMessage } from '../../types/playground.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
message: PlaygroundMessage;
|
|
4
|
+
showTimestamp?: boolean;
|
|
5
|
+
isLast?: boolean;
|
|
6
|
+
}
|
|
7
|
+
declare const MessageNotice: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type MessageNotice = ReturnType<typeof MessageNotice>;
|
|
9
|
+
export default MessageNotice;
|
|
@@ -189,6 +189,8 @@
|
|
|
189
189
|
{interrupt}
|
|
190
190
|
showTimestamp={showTimestamps}
|
|
191
191
|
onResolved={onInterruptResolved}
|
|
192
|
+
hierarchy={message.hierarchy}
|
|
193
|
+
tags={message.tags}
|
|
192
194
|
/>
|
|
193
195
|
{/if}
|
|
194
196
|
{:else}
|
|
@@ -221,6 +223,88 @@
|
|
|
221
223
|
min-height: 0;
|
|
222
224
|
overflow-y: auto;
|
|
223
225
|
padding: var(--fd-space-3xl);
|
|
226
|
+
|
|
227
|
+
/* Establish a containment context so message rows can adapt to the
|
|
228
|
+
stream's actual width (not the viewport's). The matching @container
|
|
229
|
+
queries (for .log-row) live below in the same <style> block, so
|
|
230
|
+
renaming the container only requires editing this file. */
|
|
231
|
+
container-type: inline-size;
|
|
232
|
+
container-name: fd-message-stream;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* Shared fade-in for newly-appended message rows. `-global-` so
|
|
236
|
+
ChatBubble.svelte / MessageCard.svelte can reference it without
|
|
237
|
+
redeclaring. Honour reduced-motion in the same place. */
|
|
238
|
+
@keyframes -global-fd-fade-in {
|
|
239
|
+
from {
|
|
240
|
+
opacity: 0;
|
|
241
|
+
transform: translateY(6px);
|
|
242
|
+
}
|
|
243
|
+
to {
|
|
244
|
+
opacity: 1;
|
|
245
|
+
transform: translateY(0);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@media (prefers-reduced-motion: reduce) {
|
|
250
|
+
:global(.message-bubble),
|
|
251
|
+
:global(.message-card) {
|
|
252
|
+
animation: none;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Container-query reshaping for log rows. Lives next to the
|
|
257
|
+
container-name declaration so the coupling is local — selectors are
|
|
258
|
+
:global because .log-row is a sibling component's class.
|
|
259
|
+
|
|
260
|
+
Tier 1 (≤720px): two rows — level/body, then tags/timestamp.
|
|
261
|
+
Tier 2 (≤480px): collapse further; body forces internal line break. */
|
|
262
|
+
@container fd-message-stream (max-width: 720px) {
|
|
263
|
+
:global(.log-row) {
|
|
264
|
+
display: grid;
|
|
265
|
+
grid-template-columns: auto 1fr auto;
|
|
266
|
+
grid-template-areas:
|
|
267
|
+
"level body body"
|
|
268
|
+
". tags timestamp";
|
|
269
|
+
align-items: baseline;
|
|
270
|
+
row-gap: var(--fd-space-2xs);
|
|
271
|
+
column-gap: var(--fd-space-sm);
|
|
272
|
+
}
|
|
273
|
+
:global(.log-row__level) { grid-area: level; }
|
|
274
|
+
:global(.log-row__body) { grid-area: body; min-width: 0; }
|
|
275
|
+
:global(.log-row__tags) { grid-area: tags; justify-self: start; }
|
|
276
|
+
:global(.log-row__timestamp) { grid-area: timestamp; justify-self: end; }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@container fd-message-stream (max-width: 480px) {
|
|
280
|
+
:global(.log-row) {
|
|
281
|
+
grid-template-columns: auto 1fr;
|
|
282
|
+
grid-template-areas:
|
|
283
|
+
"level body"
|
|
284
|
+
". tags";
|
|
285
|
+
}
|
|
286
|
+
:global(.log-row__text) {
|
|
287
|
+
flex-basis: 100%;
|
|
288
|
+
min-width: 0;
|
|
289
|
+
}
|
|
290
|
+
:global(.log-row__timestamp) {
|
|
291
|
+
display: none;
|
|
292
|
+
}
|
|
293
|
+
/* Drop the source + node chips: source is implied by the level
|
|
294
|
+
colour, node duplicates the hierarchy trail's last entry. Keeping
|
|
295
|
+
them at this width forced each chip onto its own line and made
|
|
296
|
+
log rows 5–6 lines tall. */
|
|
297
|
+
:global(.log-row__source),
|
|
298
|
+
:global(.log-row__node) {
|
|
299
|
+
display: none;
|
|
300
|
+
}
|
|
301
|
+
/* Reclaim horizontal room by tightening the row's own padding —
|
|
302
|
+
can't shrink the stream's padding from inside its own
|
|
303
|
+
container query. */
|
|
304
|
+
:global(.log-row) {
|
|
305
|
+
padding-left: var(--fd-space-xs);
|
|
306
|
+
padding-right: var(--fd-space-xs);
|
|
307
|
+
}
|
|
224
308
|
}
|
|
225
309
|
|
|
226
310
|
.message-stream__typing {
|
|
@@ -277,7 +361,7 @@
|
|
|
277
361
|
|
|
278
362
|
@media (max-width: 640px) {
|
|
279
363
|
.message-stream {
|
|
280
|
-
padding: var(--fd-space-
|
|
364
|
+
padding: var(--fd-space-md) 0;
|
|
281
365
|
}
|
|
282
366
|
}
|
|
283
367
|
</style>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MessageTagChip Component
|
|
3
|
+
|
|
4
|
+
Renders a single server-emitted MessageTag as a compact chip. Semantic
|
|
5
|
+
color comes from tag.color, visual emphasis from tag.variant. Used by
|
|
6
|
+
MessageBubble and InterruptBubble.
|
|
7
|
+
|
|
8
|
+
Styling: a single base rule reads from CSS custom properties; one rule
|
|
9
|
+
per color sets --chip-c, one rule per variant sets bg/fg/border in terms
|
|
10
|
+
of --chip-c. Adding a color is one line.
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import Icon from '@iconify/svelte';
|
|
15
|
+
import type { MessageTag } from '../../types/playground.js';
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
tag: MessageTag;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { tag }: Props = $props();
|
|
22
|
+
|
|
23
|
+
const color = $derived(tag.color ?? 'muted');
|
|
24
|
+
const variant = $derived(tag.variant ?? 'subtle');
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<span
|
|
28
|
+
class="message-tag-chip"
|
|
29
|
+
data-color={color}
|
|
30
|
+
data-variant={variant}
|
|
31
|
+
aria-label={tag.type ? `${tag.type}: ${tag.label}` : undefined}
|
|
32
|
+
>
|
|
33
|
+
{#if tag.icon}
|
|
34
|
+
<Icon icon={tag.icon} class="message-tag-chip__icon" aria-hidden="true" />
|
|
35
|
+
{/if}
|
|
36
|
+
<span class="message-tag-chip__label">{tag.label}</span>
|
|
37
|
+
</span>
|
|
38
|
+
|
|
39
|
+
<style>
|
|
40
|
+
.message-tag-chip {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: var(--fd-space-3xs);
|
|
44
|
+
padding: 0 var(--fd-space-3xs);
|
|
45
|
+
border-radius: var(--fd-radius-sm);
|
|
46
|
+
font-family: var(--fd-font-mono);
|
|
47
|
+
font-size: var(--fd-text-2xs);
|
|
48
|
+
line-height: 1.4;
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
min-width: 0;
|
|
51
|
+
max-width: 100%;
|
|
52
|
+
background-color: var(--chip-bg);
|
|
53
|
+
color: var(--chip-fg);
|
|
54
|
+
border: 1px solid var(--chip-border, transparent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.message-tag-chip__label {
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
text-overflow: ellipsis;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.message-tag-chip :global(.message-tag-chip__icon) {
|
|
63
|
+
flex-shrink: 0;
|
|
64
|
+
font-size: 0.875em;
|
|
65
|
+
opacity: 0.8;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Color hooks — one line per color. To add a color, add a row here. */
|
|
69
|
+
.message-tag-chip[data-color='muted'] { --chip-c: var(--fd-muted-foreground); --chip-c-on: var(--fd-background); }
|
|
70
|
+
.message-tag-chip[data-color='primary'] { --chip-c: var(--fd-primary); --chip-c-on: var(--fd-primary-foreground); }
|
|
71
|
+
.message-tag-chip[data-color='success'] { --chip-c: var(--fd-success, oklch(55% 0.15 145)); --chip-c-on: white; }
|
|
72
|
+
.message-tag-chip[data-color='warning'] { --chip-c: var(--fd-warning); --chip-c-on: var(--fd-background); }
|
|
73
|
+
.message-tag-chip[data-color='error'] { --chip-c: var(--fd-error); --chip-c-on: white; }
|
|
74
|
+
.message-tag-chip[data-color='info'] { --chip-c: var(--fd-info); --chip-c-on: var(--fd-background); }
|
|
75
|
+
|
|
76
|
+
/* Variants — derive bg/fg/border from --chip-c. */
|
|
77
|
+
.message-tag-chip[data-variant='subtle'] {
|
|
78
|
+
--chip-bg: color-mix(in srgb, var(--chip-c) 14%, transparent);
|
|
79
|
+
--chip-fg: var(--chip-c);
|
|
80
|
+
}
|
|
81
|
+
.message-tag-chip[data-variant='subtle'][data-color='muted'] {
|
|
82
|
+
/* Muted is the only color we render against the design's --fd-muted
|
|
83
|
+
surface for legibility; the color-mix path would lose contrast. */
|
|
84
|
+
--chip-bg: var(--fd-muted);
|
|
85
|
+
--chip-fg: var(--fd-muted-foreground);
|
|
86
|
+
}
|
|
87
|
+
.message-tag-chip[data-variant='solid'] {
|
|
88
|
+
--chip-bg: var(--chip-c);
|
|
89
|
+
--chip-fg: var(--chip-c-on);
|
|
90
|
+
}
|
|
91
|
+
.message-tag-chip[data-variant='outline'] {
|
|
92
|
+
--chip-bg: transparent;
|
|
93
|
+
--chip-fg: var(--chip-c);
|
|
94
|
+
--chip-border: var(--chip-c);
|
|
95
|
+
}
|
|
96
|
+
.message-tag-chip[data-variant='outline'][data-color='muted'] {
|
|
97
|
+
--chip-border: var(--fd-border);
|
|
98
|
+
}
|
|
99
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MessageTag } from '../../types/playground.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
tag: MessageTag;
|
|
4
|
+
}
|
|
5
|
+
declare const MessageTagChip: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type MessageTagChip = ReturnType<typeof MessageTagChip>;
|
|
7
|
+
export default MessageTagChip;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
MessageTagStrip Component
|
|
3
|
+
|
|
4
|
+
A flex-wrapped row of MessageTagChips. Encapsulates the layout the four
|
|
5
|
+
message variants used to copy individually.
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import type { MessageTag } from '../../types/playground.js';
|
|
10
|
+
import MessageTagChip from './MessageTagChip.svelte';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
tags: MessageTag[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { tags }: Props = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
{#if tags.length > 0}
|
|
20
|
+
<!-- No role/label — each chip is already labelled (via tag.type when set)
|
|
21
|
+
and a one-element group adds nothing for AT users. -->
|
|
22
|
+
<span class="message-tag-strip">
|
|
23
|
+
{#each tags as tag (tag.id)}
|
|
24
|
+
<MessageTagChip {tag} />
|
|
25
|
+
{/each}
|
|
26
|
+
</span>
|
|
27
|
+
{/if}
|
|
28
|
+
|
|
29
|
+
<style>
|
|
30
|
+
.message-tag-strip {
|
|
31
|
+
display: inline-flex;
|
|
32
|
+
flex-wrap: wrap;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: var(--fd-space-2xs);
|
|
35
|
+
min-width: 0;
|
|
36
|
+
}
|
|
37
|
+
</style>
|