Skip to content

Commit 6d11fbf

Browse files
marevolclaude
andauthored
Review UI improvements using Atlassian Design System (#71)
* feat(ui): enhance UI with Atlassian Design System-inspired improvements Implement comprehensive UI enhancements based on Atlassian Design System principles: - Add design token system with semantic status colors (success, info, warning, error) and spacing scale for consistent design across light/dark modes - Create Lozenge component for subtle status indicators and tags - Create unified Banner component supporting multiple intents (info, warning, success, error) - Refactor ErrorBanner to use new Banner component - Replace emoji icons with proper Fluent UI icons in ProcessingStatus component - Add Lozenge badges for counts and scores in processing status - Improve visual hierarchy and readability in status components - Enhance QueryInput with smooth transitions and better typography - Add utility classes for interactive hover states and focus rings These improvements enhance visual consistency, accessibility, and user experience while maintaining compatibility with the existing Fluent UI design system. * fix(ui): improve code quality with type safety and formatting fixes Replace `any` types with proper TypeScript types in test setup and utility files to improve type safety. Fix ESLint configuration to ignore next-env.d.ts. Apply consistent code formatting across component and test files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: improve snippet display with configurable truncation and update configs - Add truncateSnippet function to sanitize and truncate long snippets (UI) - Add NEXT_PUBLIC_SNIPPET_MAX_LENGTH env variable for configurable snippet length - Update Pydantic models from v1 Config class to v2 model_config pattern (API) - Fix test to use content= instead of deprecated data= parameter (API) - Remove redundant @types/dompurify (dompurify has built-in types) - Update tsconfig.json to use jsx: react-jsx and include dev types - Move Fess port mapping from dev to production compose - Add Fess Java opts for crawler cache and highlighting configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(ui): harden HTML sanitization against bypass attacks Add escapeHtml function to encode meta-characters and prevent XSS when using dangerouslySetInnerHTML with DOM-extracted text. Improve server-side tag stripping with iterative removal to handle malformed or overlapping tags. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent ffa4fbb commit 6d11fbf

27 files changed

+673
-221
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ CORS_ORIGINS=http://localhost:3000
8787
# API base path for Next.js frontend
8888
NEXT_PUBLIC_API_BASE=/api/v1
8989

90+
# Maximum character length for snippet display in search results
91+
# Snippets longer than this will be truncated with "..."
92+
NEXT_PUBLIC_SNIPPET_MAX_LENGTH=100
93+
9094
# ===== Logging =====
9195
LOG_LEVEL=INFO
9296
LOG_PII_MASKING=true

compose.dev.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ services:
1919
ports:
2020
- "9200:9200"
2121

22-
fess:
23-
ports:
24-
- "8080:8080"
25-
2622
ollama:
2723
ports:
2824
- "11434:11434"

compose.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,14 @@ services:
6666
environment:
6767
- SEARCH_ENGINE_HTTP_URL=http://intaste-opensearch:9200
6868
- FESS_DICTIONARY_PATH=${FESS_DICTIONARY_PATH:-/usr/share/opensearch/config/dictionary/}
69-
- "FESS_JAVA_OPTS=-Dfess.config.crawler.document.cache.enabled=false -Dfess.config.query.highlight.fragment.size=500 -Dfess.config.query.highlight.number.of.fragments=3"
69+
- "FESS_JAVA_OPTS=-Dfess.config.crawler.document.cache.enabled=false -Dfess.config.query.highlight.fragment.size=1000 -Dfess.config.query.highlight.number.of.fragments=3 -Dfess.config.query.highlight.tag.pre= -Dfess.config.query.highlight.tag.post="
7070
- TZ=${TZ:-UTC}
7171
networks:
7272
- intaste-net
7373
expose:
7474
- "8080"
75+
ports:
76+
- "8080:8080"
7577
healthcheck:
7678
<<: *curl-health
7779
test: ["CMD-SHELL", "curl -fsS 'http://localhost:8080/api/v1/health' || exit 1"]

intaste-api/app/core/llm/prompts/models.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,14 @@
66
- Automatic parameter validation via Pydantic
77
"""
88

9-
from pydantic import BaseModel, Field
9+
from pydantic import BaseModel, ConfigDict, Field
1010

1111

1212
# Base class for all prompt parameters
1313
class PromptParams(BaseModel):
1414
"""Base class for prompt template parameters."""
1515

16-
class Config:
17-
"""Pydantic config."""
18-
19-
extra = "forbid" # Prevent extra parameters
16+
model_config = ConfigDict(extra="forbid") # Prevent extra parameters
2017

2118

2219
# Intent extraction parameters
@@ -112,10 +109,7 @@ class PromptTemplate[P: PromptParams](BaseModel):
112109
default_factory=dict, description="Additional metadata (tags, author, etc.)"
113110
)
114111

115-
class Config:
116-
"""Pydantic config."""
117-
118-
frozen = True # Make templates immutable
112+
model_config = ConfigDict(frozen=True) # Make templates immutable
119113

120114
def format(self, params: P) -> str:
121115
"""Format the user template with validated parameters.

intaste-api/tests/integration/test_models_endpoints.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def test_select_model_with_malformed_json(self, client, auth_headers):
309309
"""Test selecting model with malformed JSON."""
310310
response = client.post(
311311
"/api/v1/models/select",
312-
data="not-valid-json",
312+
content="not-valid-json",
313313
headers={**auth_headers, "Content-Type": "application/json"},
314314
)
315315

intaste-ui/eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import tseslint from "typescript-eslint";
55

66
export default tseslint.config(
77
{
8-
ignores: [".next/**", "node_modules/**"],
8+
ignores: [".next/**", "node_modules/**", "next-env.d.ts"],
99
},
1010
...tseslint.configs.recommended,
1111
{

intaste-ui/package-lock.json

Lines changed: 0 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

intaste-ui/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
"dependencies": {
2121
"@fluentui/react-components": "^9.72.4",
2222
"@fluentui/react-icons": "^2.0.313",
23-
"@types/dompurify": "^3.2.0",
2423
"clsx": "^2.1.0",
2524
"dompurify": "^3.3.0",
2625
"i18next": "^25.6.3",
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright (c) 2025 CodeLibs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
'use client';
14+
15+
import {
16+
MessageBar,
17+
MessageBarBody,
18+
MessageBarTitle,
19+
MessageBarActions,
20+
Button,
21+
makeStyles,
22+
type MessageBarIntent,
23+
} from '@fluentui/react-components';
24+
import {
25+
DismissRegular,
26+
CheckmarkCircleRegular,
27+
InfoRegular,
28+
WarningRegular,
29+
ErrorCircleRegular,
30+
} from '@fluentui/react-icons';
31+
32+
const useStyles = makeStyles({
33+
actionButton: {
34+
minWidth: 'auto',
35+
},
36+
});
37+
38+
export type BannerIntent = 'info' | 'warning' | 'success' | 'error';
39+
40+
interface BannerProps {
41+
intent: BannerIntent;
42+
title?: string;
43+
message: string;
44+
onAction?: () => void;
45+
actionLabel?: string;
46+
onDismiss?: () => void;
47+
className?: string;
48+
}
49+
50+
const iconMap: Record<BannerIntent, React.ReactElement> = {
51+
success: <CheckmarkCircleRegular />,
52+
info: <InfoRegular />,
53+
warning: <WarningRegular />,
54+
error: <ErrorCircleRegular />,
55+
};
56+
57+
/**
58+
* Banner component inspired by Atlassian Design System
59+
* Displays important messages with various intent levels
60+
*/
61+
export function Banner({
62+
intent,
63+
title,
64+
message,
65+
onAction,
66+
actionLabel,
67+
onDismiss,
68+
className,
69+
}: BannerProps) {
70+
const styles = useStyles();
71+
72+
// Map our intent to Fluent UI's MessageBarIntent
73+
const fluentIntent: MessageBarIntent = intent;
74+
75+
return (
76+
<MessageBar intent={fluentIntent} icon={iconMap[intent]} className={className}>
77+
<MessageBarBody>
78+
{title && <MessageBarTitle>{title}</MessageBarTitle>}
79+
{message}
80+
</MessageBarBody>
81+
<MessageBarActions
82+
containerAction={
83+
onDismiss ? (
84+
<Button
85+
appearance="transparent"
86+
icon={<DismissRegular />}
87+
onClick={onDismiss}
88+
aria-label="Dismiss"
89+
size="small"
90+
/>
91+
) : undefined
92+
}
93+
>
94+
{onAction && actionLabel && (
95+
<Button
96+
appearance="transparent"
97+
onClick={onAction}
98+
className={styles.actionButton}
99+
size="small"
100+
>
101+
{actionLabel}
102+
</Button>
103+
)}
104+
</MessageBarActions>
105+
</MessageBar>
106+
);
107+
}

intaste-ui/src/components/common/ErrorBanner.tsx

Lines changed: 14 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,8 @@
1212

1313
'use client';
1414

15-
import {
16-
MessageBar,
17-
MessageBarBody,
18-
MessageBarTitle,
19-
MessageBarActions,
20-
Button,
21-
makeStyles,
22-
} from '@fluentui/react-components';
23-
import { DismissRegular } from '@fluentui/react-icons';
2415
import { useTranslation } from '@/libs/i18n/client';
25-
26-
const useStyles = makeStyles({
27-
retryButton: {
28-
minWidth: 'auto',
29-
},
30-
});
16+
import { Banner } from './Banner';
3117

3218
interface ErrorBannerProps {
3319
message: string;
@@ -36,40 +22,22 @@ interface ErrorBannerProps {
3622
className?: string;
3723
}
3824

25+
/**
26+
* ErrorBanner component - now using the unified Banner component
27+
* Displays error messages with optional retry and dismiss actions
28+
*/
3929
export function ErrorBanner({ message, onRetry, onDismiss, className }: ErrorBannerProps) {
4030
const { t } = useTranslation();
41-
const styles = useStyles();
4231

4332
return (
44-
<MessageBar intent="error" className={className}>
45-
<MessageBarBody>
46-
<MessageBarTitle>{t('error.title')}</MessageBarTitle>
47-
{message}
48-
</MessageBarBody>
49-
<MessageBarActions
50-
containerAction={
51-
onDismiss ? (
52-
<Button
53-
appearance="transparent"
54-
icon={<DismissRegular />}
55-
onClick={onDismiss}
56-
aria-label="Dismiss error"
57-
size="small"
58-
/>
59-
) : undefined
60-
}
61-
>
62-
{onRetry && (
63-
<Button
64-
appearance="transparent"
65-
onClick={onRetry}
66-
className={styles.retryButton}
67-
size="small"
68-
>
69-
{t('error.retry')}
70-
</Button>
71-
)}
72-
</MessageBarActions>
73-
</MessageBar>
33+
<Banner
34+
intent="error"
35+
title={t('error.title')}
36+
message={message}
37+
onAction={onRetry}
38+
actionLabel={onRetry ? t('error.retry') : undefined}
39+
onDismiss={onDismiss}
40+
className={className}
41+
/>
7442
);
7543
}

0 commit comments

Comments
 (0)