Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ui/user/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,14 @@
}
}

& table + p {
margin-top: 1rem;
}

& hr {
margin-bottom: 1rem;
}

& code {
background-color: var(--surface1);
padding: 0.25rem 0.5rem;
Expand Down
6 changes: 4 additions & 2 deletions ui/user/src/lib/components/Navbar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
unauthorized?: boolean;
hideProfileButton?: boolean;
chat?: boolean;
workflow?: boolean;
}

let {
Expand All @@ -20,7 +21,8 @@
class: klass,
unauthorized,
hideProfileButton,
chat
chat,
workflow
}: Props = $props();
</script>

Expand All @@ -29,7 +31,7 @@
{#if leftContent}
{@render leftContent()}
{:else}
<BetaLogo {chat} />
<BetaLogo {chat} {workflow} />
{/if}
<div class="flex grow items-center justify-center">
{#if centerContent}
Expand Down
71 changes: 65 additions & 6 deletions ui/user/src/lib/components/admin/MarkdownTextEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { Bold, Italic, Strikethrough } from 'lucide-svelte';
import { TooltipProvider } from '@milkdown/plugin-tooltip';
import { tooltipFactory } from '@milkdown/plugin-tooltip';
import { type Ctx } from '@milkdown/ctx';
import { type Ctx, type MilkdownPlugin } from '@milkdown/ctx';
import { toggleStrongCommand, toggleEmphasisCommand } from '@milkdown/kit/preset/commonmark';
import { toggleStrikethroughCommand } from '@milkdown/kit/preset/gfm';

Expand All @@ -19,9 +19,19 @@
onUpdate?: (markdown: string) => void;
onCancel?: () => void;
initialFocus?: boolean;
placeholder?: string;
plugins?: MilkdownPlugin[];
}

let { value = $bindable(''), class: klass, onUpdate, onCancel, initialFocus }: Props = $props();
let {
value = $bindable(''),
class: klass,
onUpdate,
onCancel,
initialFocus,
placeholder = 'Add content...',
plugins = []
}: Props = $props();

let ttDiv: HTMLDivElement | undefined = $state();
let provider: TooltipProvider | undefined = $derived.by(() => {
Expand Down Expand Up @@ -75,11 +85,12 @@
root: node,
defaultValue: value,
features: {
[Crepe.Feature.Toolbar]: false
[Crepe.Feature.Toolbar]: false,
[Crepe.Feature.Latex]: false
}
});

crepe.editor
const editorConfig = crepe.editor
.config((ctx) => {
editorCtx = ctx;

Expand All @@ -104,6 +115,11 @@
.use(listener)
.use(tooltip);

// Apply any additional plugins
for (const plugin of plugins) {
editorConfig.use(plugin);
}

crepe.create();

// Focus the editor if initialFocus is true
Expand All @@ -130,6 +146,7 @@

<div
class={klass}
style="--placeholder: '{placeholder}'"
use:editor
onfocusout={() => {
if (value !== lastSetValue) {
Expand Down Expand Up @@ -372,12 +389,12 @@
.milkdown .crepe-placeholder::before,
.milkdown [data-placeholder]::before,
.milkdown .ProseMirror[data-placeholder]:empty::before {
content: 'Add description here...' !important;
content: var(--placeholder) !important;
}

/* Additional selectors to catch different placeholder implementations */
.milkdown .ProseMirror:empty::before {
content: 'Add description here...' !important;
content: var(--placeholder) !important;
color: var(--color-gray-400);
pointer-events: none;
position: absolute;
Expand All @@ -388,5 +405,47 @@
.dark .milkdown .ProseMirror:empty::before {
color: var(--color-gray-600);
}

/* Variable pill styling */
.milkdown .ProseMirror .variable-pill {
background-color: color-mix(in oklab, var(--color-primary) 15%, transparent);
color: var(--color-primary);
padding: 0.125rem 0.5rem;
border-radius: 9999px;
font-size: 0.875rem;
font-weight: 500;
white-space: nowrap;
}

/* Completed variable pill - behaves as atomic unit */
.milkdown .ProseMirror .variable-pill-completed {
background-color: color-mix(in oklab, var(--color-primary) 25%, transparent);
cursor: default;
user-select: all;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}

/* Delete button for completed variable pills */
.milkdown .ProseMirror .variable-pill-delete {
display: inline-flex;
align-items: center;
justify-content: center;
margin-right: 0.125rem;
border-top-right-radius: 9999px;
border-bottom-right-radius: 9999px;
padding: 0.125rem 0.5rem 0.125rem 0rem;
color: var(--color-primary);
background-color: color-mix(in oklab, var(--color-primary) 25%, transparent);
cursor: pointer;
transition: background-color 0.15s ease;
font-size: 0.875rem;
font-weight: 300;
height: 23px;
}

.milkdown .ProseMirror .variable-pill-delete:hover {
color: var(--color-on-background);
}
}
</style>
12 changes: 10 additions & 2 deletions ui/user/src/lib/components/messages/Input.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import type { EditorItem } from '$lib/services/editor/index.svelte';

import PlaintextEditor from './PlaintextEditor.svelte';
import { twMerge } from 'tailwind-merge';

interface Props {
id?: string;
Expand All @@ -22,6 +23,9 @@
initialValue?: string;
items?: EditorItem[];
inputPopover?: Snippet<[string]>;
classes?: {
root?: string;
};
}

let {
Expand All @@ -38,7 +42,8 @@
placeholder = 'Your message...',
initialValue,
items = $bindable([]),
inputPopover
inputPopover,
classes
}: Props = $props();

let value = $state(initialValue || '');
Expand Down Expand Up @@ -152,7 +157,10 @@
{/if}

<div
class=" focus-within:ring-primary bg-surface1 mt-4 flex h-fit max-h-[80svh] rounded-2xl focus-within:shadow-md focus-within:ring-1"
class={twMerge(
'focus-within:ring-primary bg-surface1 mt-4 flex h-fit max-h-[80svh] rounded-2xl focus-within:shadow-md focus-within:ring-1',
classes?.root
)}
>
<div class="flex min-h-full w-full flex-col" {id}>
<label for="chat" class="sr-only">Your messages</label>
Expand Down
11 changes: 8 additions & 3 deletions ui/user/src/lib/components/navbar/BetaLogo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,32 @@
interface Props {
chat?: boolean;
enterprise?: boolean;
workflow?: boolean;
class?: string;
}
let { chat, enterprise, class: klass }: Props = $props();
let { chat, enterprise, workflow, class: klass }: Props = $props();

let logos = $derived({
dark: {
chat: appPreferences.current.logos?.darkLogoChat,
enterprise: appPreferences.current.logos?.darkLogoEnterprise,
default: appPreferences.current.logos?.darkLogoDefault
default: appPreferences.current.logos?.darkLogoDefault,
workflow: appPreferences.current.logos?.darkLogoWorkflow
},
light: {
chat: appPreferences.current.logos?.logoChat,
enterprise: appPreferences.current.logos?.logoEnterprise,
default: appPreferences.current.logos?.logoDefault
default: appPreferences.current.logos?.logoDefault,
workflow: appPreferences.current.logos?.logoWorkflow
}
});

const logoSrc = $derived.by(() => {
const theme = darkMode.isDark ? 'dark' : 'light';
if (chat) {
return logos[theme].chat;
} else if (workflow) {
return logos[theme].workflow;
} else if (enterprise) {
return logos[theme].enterprise;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { GripVerticalIcon } from 'lucide-svelte';
import { getDraggableItemContext } from './contextItem';
import { getDraggableContext } from './contextRoot';
import { tooltip } from '$lib/actions/tooltip.svelte';

const rootContext = getDraggableContext();
const itemContext = getDraggableItemContext();
Expand All @@ -14,7 +15,7 @@

<button
class={twMerge(
'draggable-handle flex h-10 cursor-move touch-none items-center justify-center select-none',
'draggable-handle flex h-10 cursor-grab touch-none items-center justify-center select-none',
isDisabled && 'pointer-events-none opacity-50',
klass
)}
Expand All @@ -34,6 +35,7 @@

return itemContext?.state?.onPointerLeave?.(ev);
}}
use:tooltip={'Drag to move'}
>
<GripVerticalIcon class="aspect-square h-full" />
</button>
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
id?: string;
data?: unknown;
children?: Snippet;
hidePointerBorder?: boolean;
};

const draggableContext = getDraggableContext();
Expand All @@ -28,7 +29,8 @@
index = 0,
id = (Date.now() * Math.random() + index).toString(16),
data,
children
children,
hidePointerBorder
}: Props = $props();

let isPointerDown = $state(false);
Expand All @@ -41,6 +43,38 @@
draggableContext.state.sourceItemId && draggableContext.state.sourceItemId === id
);

// Calculate if this item should shift up or down during drag
let shiftDirection = $derived.by(() => {
const { sourceItemId, sourceItemIndex, targetItemIndex } = draggableContext.state;

// No dragging happening or this is the dragged item
if (!sourceItemId || isActive) return 0;

// Get current item's index
const currentIndex = draggableContext.methods.getItemIndex(id);
if (currentIndex === -1) return 0;

// No valid target yet
if (targetItemIndex === -1) return 0;

// Moving DOWN (source is above target)
if (sourceItemIndex < targetItemIndex) {
// Items between source and target (inclusive) should shift UP
if (currentIndex > sourceItemIndex && currentIndex <= targetItemIndex) {
return -1; // shift up
}
}
// Moving UP (source is below target)
else if (sourceItemIndex > targetItemIndex) {
// Items between target and source (inclusive) should shift DOWN
if (currentIndex >= targetItemIndex && currentIndex < sourceItemIndex) {
return 1; // shift down
}
}

return 0;
});

let top: number | undefined = $state();

let pageY: number | undefined = $state(0);
Expand Down Expand Up @@ -123,6 +157,8 @@

pageY = ev.pageY;

// Report this item's height before setting as source
draggableContext.methods.setSourceItemHeight(rootElement.offsetHeight);
draggableContext.methods.setSourceItem(id);

isPointerDown = true;
Expand Down Expand Up @@ -151,39 +187,24 @@
class={twMerge(
'draggable-element relative min-w-full touch-none',
isActive && 'pointer-events-none z-10 cursor-move',
shiftDirection !== 0 && !isActive && 'transition-transform duration-200 ease-out',
rootClass
)}
data-id={id}
style:top={`${top ?? 0}px`}
style:transform={isPointerDown ? `translateY(${dy}px)` : ''}
onpointerenter={(ev) => {
ev.preventDefault();
if (!draggableContext.state.sourceItemId) return;
if (isActive) {
ev.stopPropagation();
return;
}

isDragOver = true;

draggableContext.methods.setTargetItem(id);
}}
onpointerleave={(ev) => {
ev.preventDefault();
if (!draggableContext.state.sourceItemId) return;
if (isActive) return;

if (draggableContext.state.targetItemId === id) {
draggableContext.methods.setTargetItem(undefined);
}
}}
style:transform={isPointerDown
? `translateY(${dy + draggableContext.state.scrollDelta}px)`
: shiftDirection !== 0
? `translateY(${shiftDirection * (draggableContext.state.sourceItemHeight + draggableContext.state.gap)}px)`
: ''}
>
<div
bind:this={containerElement}
class={twMerge(
'draggable-inner-element relative isolate z-[1] flex justify-start gap-2 rounded-sm border border-transparent transition-colors duration-200',
isPointerEntered && 'border-primary bg-primary/5',
!isActive && isDragOver && 'bg-surface2 pointer-events-none',
isPointerEntered && !hidePointerBorder && 'border-primary bg-primary/5',
!isActive && isDragOver && 'pointer-events-none',
!isActive && isDragOver && !hidePointerBorder && 'bg-surface2',
klass
)}
>
Expand Down
Loading