mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2025-10-27 08:21:30 +00:00
Always show message actions for mobile UI + improvements for user message sizing (#16076)
This commit is contained in:
committed by
GitHub
parent
d12a983659
commit
5d0a40f390
4
.gitignore
vendored
4
.gitignore
vendored
@@ -149,6 +149,6 @@ poetry.toml
|
|||||||
/run-chat.sh
|
/run-chat.sh
|
||||||
.ccache/
|
.ccache/
|
||||||
|
|
||||||
# Code Workspace
|
# IDE
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
.windsurf/
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
trigger: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Tailwind & CSS
|
|
||||||
|
|
||||||
- We are using Tailwind v4 which uses oklch colors so we now want to refer to the CSS vars directly, without wrapping it with any color function like `hsla/hsl`, `rgba` etc.
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
---
|
|
||||||
trigger: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
# Coding rules
|
|
||||||
|
|
||||||
## Svelte & SvelteKit
|
|
||||||
|
|
||||||
### Services vs Stores Separation Pattern
|
|
||||||
|
|
||||||
#### `lib/services/` - Pure Business Logic
|
|
||||||
|
|
||||||
- **Purpose**: Stateless business logic and external communication
|
|
||||||
- **Contains**:
|
|
||||||
- API calls to external services (ApiService)
|
|
||||||
- Pure business logic functions (ChatService, etc.)
|
|
||||||
- **Rules**:
|
|
||||||
- NO Svelte runes ($state, $derived, $effect)
|
|
||||||
- NO reactive state management
|
|
||||||
- Pure functions and classes only
|
|
||||||
- Can import types but not stores
|
|
||||||
- Focus on "how" - implementation details
|
|
||||||
|
|
||||||
#### `lib/stores/` - Reactive State Management
|
|
||||||
|
|
||||||
- **Purpose**: Svelte-specific reactive state with runes
|
|
||||||
- **Contains**:
|
|
||||||
- Reactive state classes with $state, $derived, $effect
|
|
||||||
- Database operations (DatabaseStore)
|
|
||||||
- UI-focused state management
|
|
||||||
- Store orchestration logic
|
|
||||||
- **Rules**:
|
|
||||||
- USE Svelte runes for reactivity
|
|
||||||
- Import and use services for business logic
|
|
||||||
- NO direct database operations
|
|
||||||
- NO direct API calls (use services)
|
|
||||||
- Focus on "what" - reactive state for UI
|
|
||||||
|
|
||||||
#### Enforcement
|
|
||||||
|
|
||||||
- Services should be testable without Svelte
|
|
||||||
- Stores should leverage Svelte's reactivity system
|
|
||||||
- Clear separation: services handle data, stores handle state
|
|
||||||
- Services can be reused across multiple stores
|
|
||||||
|
|
||||||
#### Misc
|
|
||||||
|
|
||||||
- Always use `let` for $derived state variables
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
trigger: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
# Automated Tests
|
|
||||||
|
|
||||||
## General rules
|
|
||||||
|
|
||||||
- NEVER include any test code in the production code - we should always have it in a separate dedicated files
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
trigger: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
## TypeScript
|
|
||||||
|
|
||||||
- Add JSDocs for functions
|
|
||||||
Binary file not shown.
@@ -4,7 +4,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev --host 0.0.0.0 & storybook dev -p 6006 --ci",
|
"dev": "bash scripts/dev.sh",
|
||||||
"build": "vite build && ./scripts/post-build.sh",
|
"build": "vite build && ./scripts/post-build.sh",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"prepare": "svelte-kit sync || echo ''",
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
@@ -20,7 +20,8 @@
|
|||||||
"test:ui": "vitest --project=ui",
|
"test:ui": "vitest --project=ui",
|
||||||
"test:unit": "vitest",
|
"test:unit": "vitest",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"build-storybook": "storybook build"
|
"build-storybook": "storybook build",
|
||||||
|
"cleanup": "rm -rf .svelte-kit build node_modules test-results"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chromatic-com/storybook": "^4.0.1",
|
"@chromatic-com/storybook": "^4.0.1",
|
||||||
|
|||||||
103
tools/server/webui/scripts/dev.sh
Normal file
103
tools/server/webui/scripts/dev.sh
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd ../../../
|
||||||
|
|
||||||
|
# Check and install git hooks if missing
|
||||||
|
check_and_install_hooks() {
|
||||||
|
local hooks_missing=false
|
||||||
|
|
||||||
|
# Check for required hooks
|
||||||
|
if [ ! -f ".git/hooks/pre-commit" ] || [ ! -f ".git/hooks/pre-push" ] || [ ! -f ".git/hooks/post-push" ]; then
|
||||||
|
hooks_missing=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$hooks_missing" = true ]; then
|
||||||
|
echo "🔧 Git hooks missing, installing them..."
|
||||||
|
cd tools/server/webui
|
||||||
|
if bash scripts/install-git-hooks.sh; then
|
||||||
|
echo "✅ Git hooks installed successfully"
|
||||||
|
else
|
||||||
|
echo "⚠️ Failed to install git hooks, continuing anyway..."
|
||||||
|
fi
|
||||||
|
cd ../../../
|
||||||
|
else
|
||||||
|
echo "✅ Git hooks already installed"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install git hooks if needed
|
||||||
|
check_and_install_hooks
|
||||||
|
|
||||||
|
# Check if llama-server binary already exists
|
||||||
|
if [ ! -f "build/bin/llama-server" ]; then
|
||||||
|
echo "Building llama-server..."
|
||||||
|
cmake -B build && cmake --build build --config Release -t llama-server
|
||||||
|
else
|
||||||
|
echo "llama-server binary already exists, skipping build."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start llama-server and capture output
|
||||||
|
echo "Starting llama-server..."
|
||||||
|
mkfifo server_output.pipe
|
||||||
|
build/bin/llama-server -hf ggml-org/gpt-oss-20b-GGUF --jinja -c 0 --no-webui > server_output.pipe 2>&1 &
|
||||||
|
SERVER_PID=$!
|
||||||
|
|
||||||
|
# Function to wait for server to be ready
|
||||||
|
wait_for_server() {
|
||||||
|
echo "Waiting for llama-server to be ready..."
|
||||||
|
local max_wait=60
|
||||||
|
local start_time=$(date +%s)
|
||||||
|
|
||||||
|
# Read server output in background and look for the ready message
|
||||||
|
(
|
||||||
|
while IFS= read -r line; do
|
||||||
|
echo "🔍 Server: $line"
|
||||||
|
if [[ "$line" == *"server is listening on http://127.0.0.1:8080 - starting the main loop"* ]]; then
|
||||||
|
echo "✅ llama-server is ready!"
|
||||||
|
echo "READY" > server_ready.flag
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done < server_output.pipe
|
||||||
|
) &
|
||||||
|
|
||||||
|
# Wait for ready flag or timeout
|
||||||
|
while [ ! -f server_ready.flag ]; do
|
||||||
|
local current_time=$(date +%s)
|
||||||
|
local elapsed=$((current_time - start_time))
|
||||||
|
|
||||||
|
if [ $elapsed -ge $max_wait ]; then
|
||||||
|
echo "❌ Server failed to start within $max_wait seconds"
|
||||||
|
rm -f server_ready.flag
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f server_ready.flag
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cleanup function
|
||||||
|
cleanup() {
|
||||||
|
echo "🧹 Cleaning up..."
|
||||||
|
kill $SERVER_PID 2>/dev/null
|
||||||
|
rm -f server_output.pipe server_ready.flag
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set up signal handlers
|
||||||
|
trap cleanup SIGINT SIGTERM
|
||||||
|
|
||||||
|
# Wait for server to be ready
|
||||||
|
if wait_for_server; then
|
||||||
|
echo "🚀 Starting development servers..."
|
||||||
|
cd tools/server/webui
|
||||||
|
storybook dev -p 6006 --ci & vite dev --host 0.0.0.0 &
|
||||||
|
|
||||||
|
# Wait for all background processes
|
||||||
|
wait
|
||||||
|
else
|
||||||
|
echo "❌ Failed to start development environment"
|
||||||
|
cleanup
|
||||||
|
fi
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Script to install pre-commit and post-commit hooks for webui
|
# Script to install pre-commit and pre-push hooks for webui
|
||||||
# Pre-commit: formats, lints, checks, and builds code, stashes unstaged changes
|
# Pre-commit: formats code and runs checks
|
||||||
# Post-commit: automatically unstashes changes
|
# Pre-push: builds the project, stashes unstaged changes
|
||||||
|
|
||||||
REPO_ROOT=$(git rev-parse --show-toplevel)
|
REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||||
PRE_COMMIT_HOOK="$REPO_ROOT/.git/hooks/pre-commit"
|
PRE_COMMIT_HOOK="$REPO_ROOT/.git/hooks/pre-commit"
|
||||||
POST_COMMIT_HOOK="$REPO_ROOT/.git/hooks/post-commit"
|
PRE_PUSH_HOOK="$REPO_ROOT/.git/hooks/pre-push"
|
||||||
|
|
||||||
echo "Installing pre-commit and post-commit hooks for webui..."
|
echo "Installing pre-commit and pre-push hooks for webui..."
|
||||||
|
|
||||||
# Create the pre-commit hook
|
# Create the pre-commit hook
|
||||||
cat > "$PRE_COMMIT_HOOK" << 'EOF'
|
cat > "$PRE_COMMIT_HOOK" << 'EOF'
|
||||||
@@ -16,7 +16,7 @@ cat > "$PRE_COMMIT_HOOK" << 'EOF'
|
|||||||
|
|
||||||
# Check if there are any changes in the webui directory
|
# Check if there are any changes in the webui directory
|
||||||
if git diff --cached --name-only | grep -q "^tools/server/webui/"; then
|
if git diff --cached --name-only | grep -q "^tools/server/webui/"; then
|
||||||
echo "Formatting webui code..."
|
echo "Formatting and checking webui code..."
|
||||||
|
|
||||||
# Change to webui directory and run format
|
# Change to webui directory and run format
|
||||||
cd tools/server/webui
|
cd tools/server/webui
|
||||||
@@ -27,20 +27,12 @@ if git diff --cached --name-only | grep -q "^tools/server/webui/"; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Stash any unstaged changes to avoid conflicts during format/build
|
|
||||||
echo "Stashing unstaged changes..."
|
|
||||||
git stash push --keep-index --include-untracked -m "Pre-commit hook: stashed unstaged changes"
|
|
||||||
STASH_CREATED=$?
|
|
||||||
|
|
||||||
# Run the format command
|
# Run the format command
|
||||||
npm run format
|
npm run format
|
||||||
|
|
||||||
# Check if format command succeeded
|
# Check if format command succeeded
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "Error: npm run format failed"
|
echo "Error: npm run format failed"
|
||||||
if [ $STASH_CREATED -eq 0 ]; then
|
|
||||||
echo "You can restore your unstaged changes with: git stash pop"
|
|
||||||
fi
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -50,9 +42,6 @@ if git diff --cached --name-only | grep -q "^tools/server/webui/"; then
|
|||||||
# Check if lint command succeeded
|
# Check if lint command succeeded
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "Error: npm run lint failed"
|
echo "Error: npm run lint failed"
|
||||||
if [ $STASH_CREATED -eq 0 ]; then
|
|
||||||
echo "You can restore your unstaged changes with: git stash pop"
|
|
||||||
fi
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -62,73 +51,151 @@ if git diff --cached --name-only | grep -q "^tools/server/webui/"; then
|
|||||||
# Check if check command succeeded
|
# Check if check command succeeded
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "Error: npm run check failed"
|
echo "Error: npm run check failed"
|
||||||
if [ $STASH_CREATED -eq 0 ]; then
|
|
||||||
echo "You can restore your unstaged changes with: git stash pop"
|
|
||||||
fi
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run the build command
|
# Go back to repo root
|
||||||
npm run build
|
|
||||||
|
|
||||||
# Check if build command succeeded
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Error: npm run build failed"
|
|
||||||
if [ $STASH_CREATED -eq 0 ]; then
|
|
||||||
echo "You can restore your unstaged changes with: git stash pop"
|
|
||||||
fi
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Go back to repo root to add build output
|
|
||||||
cd ../../..
|
cd ../../..
|
||||||
|
|
||||||
# Add the build output to staging area
|
echo "✅ Webui code formatted and checked successfully"
|
||||||
git add tools/server/public/index.html.gz
|
|
||||||
|
|
||||||
if [ $STASH_CREATED -eq 0 ]; then
|
|
||||||
echo "✅ Build completed. Your unstaged changes have been stashed."
|
|
||||||
echo "They will be automatically restored after the commit."
|
|
||||||
# Create a marker file to indicate stash was created by pre-commit hook
|
|
||||||
touch .git/WEBUI_STASH_MARKER
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Webui code formatted successfully"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Create the post-commit hook
|
# Create the pre-push hook
|
||||||
cat > "$POST_COMMIT_HOOK" << 'EOF'
|
cat > "$PRE_PUSH_HOOK" << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Check if we have a stash marker from the pre-commit hook
|
# Check if there are any webui changes that need building
|
||||||
if [ -f .git/WEBUI_STASH_MARKER ]; then
|
WEBUI_CHANGES=$(git diff --name-only @{push}..HEAD | grep "^tools/server/webui/" || true)
|
||||||
echo "Restoring your unstaged changes..."
|
|
||||||
|
if [ -n "$WEBUI_CHANGES" ]; then
|
||||||
|
echo "Webui changes detected, checking if build is up-to-date..."
|
||||||
|
|
||||||
|
# Change to webui directory
|
||||||
|
cd tools/server/webui
|
||||||
|
|
||||||
|
# Check if npm is available and package.json exists
|
||||||
|
if [ ! -f "package.json" ]; then
|
||||||
|
echo "Error: package.json not found in tools/server/webui"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if build output exists and is newer than source files
|
||||||
|
BUILD_FILE="../public/index.html.gz"
|
||||||
|
NEEDS_BUILD=false
|
||||||
|
|
||||||
|
if [ ! -f "$BUILD_FILE" ]; then
|
||||||
|
echo "Build output not found, building..."
|
||||||
|
NEEDS_BUILD=true
|
||||||
|
else
|
||||||
|
# Check if any source files are newer than the build output
|
||||||
|
if find src -newer "$BUILD_FILE" -type f | head -1 | grep -q .; then
|
||||||
|
echo "Source files are newer than build output, rebuilding..."
|
||||||
|
NEEDS_BUILD=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$NEEDS_BUILD" = true ]; then
|
||||||
|
echo "Building webui..."
|
||||||
|
|
||||||
|
# Stash any unstaged changes to avoid conflicts during build
|
||||||
|
echo "Checking for unstaged changes..."
|
||||||
|
if ! git diff --quiet || ! git diff --cached --quiet --diff-filter=A; then
|
||||||
|
echo "Stashing unstaged changes..."
|
||||||
|
git stash push --include-untracked -m "Pre-push hook: stashed unstaged changes"
|
||||||
|
STASH_CREATED=$?
|
||||||
|
else
|
||||||
|
echo "No unstaged changes to stash"
|
||||||
|
STASH_CREATED=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the build command
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Check if build command succeeded
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Error: npm run build failed"
|
||||||
|
if [ $STASH_CREATED -eq 0 ]; then
|
||||||
|
echo "You can restore your unstaged changes with: git stash pop"
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Go back to repo root
|
||||||
|
cd ../../..
|
||||||
|
|
||||||
|
# Check if build output was created/updated
|
||||||
|
if [ -f "tools/server/public/index.html.gz" ]; then
|
||||||
|
# Add the build output and commit it
|
||||||
|
git add tools/server/public/index.html.gz
|
||||||
|
if ! git diff --cached --quiet; then
|
||||||
|
echo "Committing updated build output..."
|
||||||
|
git commit -m "chore: update webui build output"
|
||||||
|
echo "✅ Build output committed successfully"
|
||||||
|
else
|
||||||
|
echo "Build output unchanged"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Error: Build output not found after build"
|
||||||
|
if [ $STASH_CREATED -eq 0 ]; then
|
||||||
|
echo "You can restore your unstaged changes with: git stash pop"
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $STASH_CREATED -eq 0 ]; then
|
||||||
|
echo "✅ Build completed. Your unstaged changes have been stashed."
|
||||||
|
echo "They will be automatically restored after the push."
|
||||||
|
# Create a marker file to indicate stash was created by pre-push hook
|
||||||
|
touch .git/WEBUI_PUSH_STASH_MARKER
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✅ Build output is up-to-date"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✅ Webui ready for push"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create the post-push hook (for restoring stashed changes after push)
|
||||||
|
cat > "$REPO_ROOT/.git/hooks/post-push" << 'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Check if we have a stash marker from the pre-push hook
|
||||||
|
if [ -f .git/WEBUI_PUSH_STASH_MARKER ]; then
|
||||||
|
echo "Restoring your unstaged changes after push..."
|
||||||
git stash pop
|
git stash pop
|
||||||
rm -f .git/WEBUI_STASH_MARKER
|
rm -f .git/WEBUI_PUSH_STASH_MARKER
|
||||||
echo "✅ Your unstaged changes have been restored."
|
echo "✅ Your unstaged changes have been restored."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Make both hooks executable
|
# Make all hooks executable
|
||||||
chmod +x "$PRE_COMMIT_HOOK"
|
chmod +x "$PRE_COMMIT_HOOK"
|
||||||
chmod +x "$POST_COMMIT_HOOK"
|
chmod +x "$PRE_PUSH_HOOK"
|
||||||
|
chmod +x "$REPO_ROOT/.git/hooks/post-push"
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "✅ Pre-commit and post-commit hooks installed successfully!"
|
echo "✅ Git hooks installed successfully!"
|
||||||
echo " Pre-commit: $PRE_COMMIT_HOOK"
|
echo " Pre-commit: $PRE_COMMIT_HOOK"
|
||||||
echo " Post-commit: $POST_COMMIT_HOOK"
|
echo " Pre-push: $PRE_PUSH_HOOK"
|
||||||
|
echo " Post-push: $REPO_ROOT/.git/hooks/post-push"
|
||||||
echo ""
|
echo ""
|
||||||
echo "The hooks will automatically:"
|
echo "The hooks will automatically:"
|
||||||
echo " • Format, lint, check, and build webui code before commits"
|
echo " • Format and check webui code before commits (pre-commit)"
|
||||||
echo " • Stash unstaged changes during the process"
|
echo " • Build webui code before pushes (pre-push)"
|
||||||
echo " • Restore your unstaged changes after the commit"
|
echo " • Stash unstaged changes during build process"
|
||||||
|
echo " • Restore your unstaged changes after the push"
|
||||||
echo ""
|
echo ""
|
||||||
echo "To test the hooks, make a change to a file in the webui directory and commit it."
|
echo "To test the hooks:"
|
||||||
|
echo " • Make a change to a file in the webui directory and commit it (triggers format/check)"
|
||||||
|
echo " • Push your commits to trigger the build process"
|
||||||
else
|
else
|
||||||
echo "❌ Failed to make hooks executable"
|
echo "❌ Failed to make hooks executable"
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
<div class="relative {justify === 'start' ? 'mt-2' : ''} flex h-6 items-center justify-{justify}">
|
<div class="relative {justify === 'start' ? 'mt-2' : ''} flex h-6 items-center justify-{justify}">
|
||||||
<div
|
<div
|
||||||
class="flex items-center text-xs text-muted-foreground transition-opacity group-hover:opacity-0"
|
class="hidden items-center text-xs text-muted-foreground transition-opacity md:flex md:group-hover:opacity-0"
|
||||||
>
|
>
|
||||||
{new Date(message.timestamp).toLocaleTimeString(undefined, {
|
{new Date(message.timestamp).toLocaleTimeString(undefined, {
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
@@ -61,14 +61,14 @@
|
|||||||
<div
|
<div
|
||||||
class="absolute top-0 {actionsPosition === 'left'
|
class="absolute top-0 {actionsPosition === 'left'
|
||||||
? 'left-0'
|
? 'left-0'
|
||||||
: 'right-0'} flex items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100"
|
: 'right-0'} flex items-center gap-2 opacity-100 transition-opacity md:opacity-0 md:group-hover:opacity-100"
|
||||||
>
|
>
|
||||||
{#if siblingInfo && siblingInfo.totalSiblings > 1}
|
{#if siblingInfo && siblingInfo.totalSiblings > 1}
|
||||||
<ChatMessageBranchingControls {siblingInfo} {onNavigateToSibling} />
|
<ChatMessageBranchingControls {siblingInfo} {onNavigateToSibling} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="pointer-events-none inset-0 flex items-center gap-1 opacity-0 transition-all duration-150 group-hover:pointer-events-auto group-hover:opacity-100"
|
class="pointer-events-auto inset-0 flex items-center gap-1 opacity-100 transition-all duration-150 md:pointer-events-none md:opacity-0 md:group-hover:pointer-events-auto md:group-hover:opacity-100"
|
||||||
>
|
>
|
||||||
<ActionButton icon={Copy} tooltip="Copy" onclick={onCopy} />
|
<ActionButton icon={Copy} tooltip="Copy" onclick={onCopy} />
|
||||||
|
|
||||||
|
|||||||
@@ -52,11 +52,38 @@
|
|||||||
onShowDeleteDialogChange,
|
onShowDeleteDialogChange,
|
||||||
textareaElement = $bindable()
|
textareaElement = $bindable()
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
|
let isMultiline = $state(false);
|
||||||
|
let messageElement: HTMLElement | undefined = $state();
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (!messageElement || !message.content.trim()) return;
|
||||||
|
|
||||||
|
if (message.content.includes('\n')) {
|
||||||
|
isMultiline = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
const element = entry.target as HTMLElement;
|
||||||
|
const estimatedSingleLineHeight = 24; // Typical line height for text-md
|
||||||
|
|
||||||
|
isMultiline = element.offsetHeight > estimatedSingleLineHeight * 1.5;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resizeObserver.observe(messageElement);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
};
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
aria-label="User message with actions"
|
aria-label="User message with actions"
|
||||||
class="group flex flex-col items-end gap-2 {className}"
|
class="group flex flex-col items-end gap-3 md:gap-2 {className}"
|
||||||
role="group"
|
role="group"
|
||||||
>
|
>
|
||||||
{#if isEditing}
|
{#if isEditing}
|
||||||
@@ -92,10 +119,13 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if message.content.trim()}
|
{#if message.content.trim()}
|
||||||
<Card class="max-w-[80%] rounded-2xl bg-primary px-2.5 py-1.5 text-primary-foreground">
|
<Card
|
||||||
<div class="text-md whitespace-pre-wrap">
|
class="max-w-[80%] rounded-[1.125rem] bg-primary px-3.75 py-1.5 text-primary-foreground data-[multiline]:py-2.5"
|
||||||
|
data-multiline={isMultiline ? '' : undefined}
|
||||||
|
>
|
||||||
|
<span bind:this={messageElement} class="text-md whitespace-pre-wrap">
|
||||||
{message.content}
|
{message.content}
|
||||||
</div>
|
</span>
|
||||||
</Card>
|
</Card>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* Base typography styles */
|
/* Base typography styles */
|
||||||
div :global(p) {
|
div :global(p:not(:last-child)) {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
line-height: 1.75;
|
line-height: 1.75;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user