Skip to content

Commit be554d7

Browse files
committed
Polish tool UI interactions
1 parent 4d1958c commit be554d7

25 files changed

Lines changed: 584 additions & 274 deletions

File tree

‎package-lock.json‎

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

‎package.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
"@codemirror/language": "^6.12.3",
2626
"@codemirror/state": "^6.6.0",
2727
"@codemirror/view": "^6.43.1",
28-
"@radix-ui/react-collapsible": "^1.1.14",
2928
"@radix-ui/react-dropdown-menu": "^2.1.18",
3029
"@radix-ui/react-toast": "^1.2.17",
3130
"@radix-ui/react-tooltip": "^1.2.10",

‎src/components/shared/editor/CodeEditor.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export default function CodeEditor({
139139
</div>
140140
<div className="code-editor__actions">
141141
{statusText ? <Badge tone={statusTone(status)}>{statusText}</Badge> : null}
142-
{notice ? <span className="code-editor__action-status" role="status" aria-live="polite">{notice}</span> : null}
142+
{notice ? <span className="copy-feedback code-editor__action-status" role="status" aria-live="polite">{notice}</span> : null}
143143
<Button variant="secondary" size="sm" disabled={isEmpty} onClick={copyValue}>{label.copy}</Button>
144144
{!isReadOnly ? <Button variant="ghost" size="sm" disabled={isEmpty} onClick={clearValue}>{label.clear}</Button> : null}
145145
</div>

‎src/components/shared/layouts/AppShell.tsx‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ export default function AppShell({ initialToolId }: AppShellProps) {
6868
</div>
6969
</header>
7070
<RefPanelProvider value={refPanelValue}>
71-
<Suspense fallback={<div className="state-box">加载中...</div>}>
72-
{ToolComponent ? <ToolComponent /> : <div className="state-box">工具组件未注册。</div>}
73-
</Suspense>
71+
<div className="tool-app-body">
72+
<Suspense fallback={<div className="state-box">加载中...</div>}>
73+
{ToolComponent ? <ToolComponent /> : <div className="state-box">工具组件未注册。</div>}
74+
</Suspense>
75+
</div>
7476
</RefPanelProvider>
7577
</section>
7678
{refContent ? (

‎src/components/shared/ui/CopyRow.tsx‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { useTransientNotice } from '../../../hooks/useTransientNotice';
55
interface CopyRowProps {
66
label: string;
77
value: string;
8+
density?: 'default' | 'compact' | 'long';
89
}
910

10-
const CopyRow = memo(function CopyRow({ label, value }: CopyRowProps) {
11+
const CopyRow = memo(function CopyRow({ label, value, density = 'default' }: CopyRowProps) {
1112
const [notice, showNotice] = useTransientNotice();
1213
const { Button } = useTheme();
1314
const isEmpty = value.length === 0;
@@ -27,13 +28,15 @@ const CopyRow = memo(function CopyRow({ label, value }: CopyRowProps) {
2728
}, [isEmpty, showNotice, value]);
2829

2930
return (
30-
<div className="copy-row">
31+
<div className={`copy-row copy-row--${density}`}>
3132
<span className="copy-row__label">{label}</span>
32-
<code className="copy-row__value">{value}</code>
33-
{notice ? <span className="copy-row__notice" role="status" aria-live="polite">{notice}</span> : null}
34-
<Button variant="ghost" size="sm" disabled={isEmpty} onClick={handleCopy}>
35-
复制
36-
</Button>
33+
<code className="copy-row__value" title={value}>{value}</code>
34+
<span className="copy-row__action">
35+
{notice ? <span className="copy-feedback copy-row__notice" role="status" aria-live="polite">{notice}</span> : null}
36+
<Button variant="ghost" size="sm" disabled={isEmpty} onClick={handleCopy}>
37+
复制
38+
</Button>
39+
</span>
3740
</div>
3841
);
3942
});
Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import * as Collapsible from '@radix-ui/react-collapsible';
2-
import { ChevronDown, ChevronLeft, ChevronRight, PanelRightClose, PanelRightOpen } from 'lucide-react';
3-
import { useState } from 'react';
1+
import { PanelRightClose, PanelRightOpen } from 'lucide-react';
42

53
interface ReferenceItem {
64
syntax: string;
@@ -20,44 +18,39 @@ interface ReferencePanelProps {
2018
}
2119

2220
export default function ReferencePanel({ title, sections, collapsed, onToggleCollapse }: ReferencePanelProps) {
23-
const [isOpen, setIsOpen] = useState(true);
2421
const isCollapsed = collapsed ?? false;
2522

2623
if (isCollapsed) {
2724
return (
28-
<div className="ref-panel ref-panel--collapsed">
29-
<button
30-
className="ref-panel__rail-toggle"
31-
type="button"
32-
onClick={onToggleCollapse}
33-
aria-label="展开参考面板"
34-
title="展开参考面板"
35-
>
36-
<ChevronLeft size={17} strokeWidth={2.2} aria-hidden="true" />
37-
<span>参考</span>
38-
</button>
39-
</div>
25+
<button
26+
className="ref-panel ref-panel--collapsed ref-panel__rail-toggle"
27+
type="button"
28+
onClick={onToggleCollapse}
29+
aria-label="展开参考面板"
30+
title="展开参考面板"
31+
>
32+
<PanelRightOpen size={17} strokeWidth={2.2} aria-hidden="true" />
33+
</button>
4034
);
4135
}
4236

4337
return (
44-
<Collapsible.Root className="ref-panel" open={isOpen} onOpenChange={setIsOpen}>
45-
<div className="ref-panel__header">
46-
<Collapsible.Trigger className="ref-panel__toggle" aria-label={isOpen ? '收起参考内容' : '展开参考内容'}>
38+
<div className="ref-panel">
39+
<button
40+
className="ref-panel__header"
41+
type="button"
42+
onClick={onToggleCollapse}
43+
aria-label="收起参考面板"
44+
title="收起参考面板"
45+
>
46+
<span className="ref-panel__title-bar">
4747
<span className="ref-panel__title">{title}</span>
48-
{isOpen ? <ChevronDown size={17} strokeWidth={2.2} aria-hidden="true" /> : <ChevronRight size={17} strokeWidth={2.2} aria-hidden="true" />}
49-
</Collapsible.Trigger>
50-
<button
51-
className="ref-panel__dock-toggle"
52-
type="button"
53-
onClick={onToggleCollapse}
54-
aria-label="收起参考面板"
55-
title="收起参考面板"
56-
>
57-
{isOpen ? <PanelRightClose size={16} strokeWidth={2.2} aria-hidden="true" /> : <PanelRightOpen size={16} strokeWidth={2.2} aria-hidden="true" />}
58-
</button>
59-
</div>
60-
<Collapsible.Content className="ref-panel__body">
48+
</span>
49+
<span className="ref-panel__dock-toggle" aria-hidden="true">
50+
<PanelRightClose size={16} strokeWidth={2.2} aria-hidden="true" />
51+
</span>
52+
</button>
53+
<div className="ref-panel__body">
6154
{sections.map((section) => (
6255
<section key={section.title} className="ref-section">
6356
<h3 className="ref-section__title">{section.title}</h3>
@@ -71,7 +64,7 @@ export default function ReferencePanel({ title, sections, collapsed, onToggleCol
7164
</div>
7265
</section>
7366
))}
74-
</Collapsible.Content>
75-
</Collapsible.Root>
67+
</div>
68+
</div>
7669
);
7770
}

‎src/components/ui/Button.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const buttonVariants = cva(
77
{
88
variants: {
99
variant: {
10-
primary: 'bg-[var(--primary)] text-[var(--text-inverse)] hover:bg-[var(--primary-strong)]',
10+
primary: 'bg-[var(--primary)] text-[var(--button-primary-text)] hover:bg-[var(--primary-strong)]',
1111
secondary: 'border border-[var(--border)] bg-[var(--surface)] text-[var(--text)] hover:bg-[var(--surface-muted)]',
1212
ghost: 'bg-transparent text-[var(--muted)] hover:bg-[var(--surface-muted)] hover:text-[var(--text)]',
1313
danger: 'bg-[var(--danger)] text-[var(--text-inverse)] hover:bg-[color-mix(in_srgb,var(--danger)_86%,black)]',

‎src/layouts/BaseLayout.astro‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import ThemeManager from '../themes/ThemeManager';
66
interface Props {
77
title: string;
88
description?: string;
9+
mainClass?: string;
910
}
1011
11-
const { title, description = 'Bytekit 开发工具站' } = Astro.props;
12+
const { title, description = 'Bytekit 开发工具站', mainClass = '' } = Astro.props;
13+
const mainClassName = ['site-main', mainClass].filter(Boolean).join(' ');
1214
---
1315

1416
<!doctype html>
@@ -38,7 +40,7 @@ const { title, description = 'Bytekit 开发工具站' } = Astro.props;
3840
<body>
3941
<ThemeManager client:load>
4042
<AppHeader />
41-
<main class="site-main">
43+
<main class={mainClassName}>
4244
<slot />
4345
</main>
4446
</ThemeManager>

‎src/pages/tools/[category]/[tool].astro‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ const { tool: toolSlug, category } = Astro.params;
1616
const tool = tools.find((t) => t.href === `tools/${category}/${toolSlug}`);
1717
---
1818

19-
<BaseLayout title={`${tool?.name ?? '工具'} - Bytekit`} description={tool?.description ?? ''}>
19+
<BaseLayout title={`${tool?.name ?? '工具'} - Bytekit`} description={tool?.description ?? ''} mainClass="site-main--tool">
2020
<AppShell client:load initialToolId={tool?.id ?? 'json'} />
2121
</BaseLayout>

‎src/themes/base.css‎

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ textarea:focus-visible {
134134

135135
.ui-button--primary {
136136
background: var(--accent-primary);
137-
color: var(--text-inverse);
137+
color: var(--button-primary-text);
138138
}
139139

140140
.ui-button--primary:hover {
@@ -530,6 +530,12 @@ textarea:focus-visible {
530530
padding: var(--layout-page-block) var(--layout-page-inline) clamp(1.75rem, 3vw, 2.5rem);
531531
}
532532

533+
.site-main--tool {
534+
width: 100%;
535+
max-width: none;
536+
padding: var(--space-2) 0 clamp(1.25rem, 2vw, 2rem);
537+
}
538+
533539
.home-shell {
534540
display: grid;
535541
grid-template-columns: clamp(12rem, 14vw, 15rem) minmax(0, 1fr);
@@ -812,13 +818,13 @@ textarea:focus-visible {
812818
.tool-app-shell {
813819
display: grid;
814820
grid-template-columns: auto 1fr auto;
815-
gap: var(--space-4);
821+
gap: 0;
816822
min-width: 0;
817823
}
818824

819825
.tool-ref-sidebar {
820826
position: sticky;
821-
top: calc(3.75rem + var(--layout-page-block));
827+
top: calc(3.75rem + var(--space-2));
822828
display: flex;
823829
flex-direction: column;
824830
gap: 0;
@@ -839,25 +845,43 @@ textarea:focus-visible {
839845
display: flex;
840846
flex-direction: column;
841847
align-items: center;
848+
height: 100%;
842849
width: 100%;
843850
}
844851

845852
.tool-app-content {
853+
--tool-content-max: 78rem;
846854
min-width: 0;
847855
display: grid;
848-
gap: var(--space-4);
856+
gap: var(--space-3);
849857
align-content: start;
858+
padding: 0 var(--space-3);
850859
}
851860

852861
.tool-app-head {
853862
display: flex;
854863
align-items: end;
855864
justify-content: space-between;
856-
gap: var(--space-4);
857-
padding-bottom: var(--space-2);
865+
gap: var(--space-3);
866+
width: min(100%, var(--tool-content-max));
867+
justify-self: center;
868+
padding-bottom: var(--space-1);
858869
border-bottom: 1px solid var(--border);
859870
}
860871

872+
.tool-app-body {
873+
display: grid;
874+
justify-items: center;
875+
width: 100%;
876+
min-width: 0;
877+
}
878+
879+
.tool-app-body > * {
880+
width: min(100%, var(--tool-content-max));
881+
min-width: 0;
882+
justify-self: center;
883+
}
884+
861885
/* ─── FloatingPanel (unified sidebar + reference panel) ─── */
862886

863887
.fp {
@@ -956,14 +980,14 @@ textarea:focus-visible {
956980

957981
.tool-sidebar {
958982
position: sticky;
959-
top: calc(3.75rem + var(--layout-page-block));
983+
top: calc(3.75rem + var(--space-2));
960984
display: flex;
961985
flex-direction: column;
962-
gap: var(--space-2);
986+
gap: var(--space-1);
963987
width: var(--size-sidebar);
964988
max-height: calc(100vh - 5.5rem);
965989
overflow: hidden;
966-
padding: var(--space-3);
990+
padding: var(--space-2);
967991
border-right: 1px solid var(--border);
968992
scrollbar-width: thin;
969993
transition: width var(--motion-normal) var(--ease-standard);
@@ -979,7 +1003,7 @@ textarea:focus-visible {
9791003
align-items: center;
9801004
justify-content: space-between;
9811005
width: 100%;
982-
min-height: 2rem;
1006+
min-height: 2.25rem;
9831007
padding: 0 var(--space-2);
9841008
border-radius: var(--radius-sm);
9851009
background: transparent;
@@ -993,12 +1017,11 @@ textarea:focus-visible {
9931017
}
9941018

9951019
.tool-sidebar--collapsed .tool-sidebar__head {
996-
writing-mode: vertical-rl;
9971020
justify-content: center;
9981021
align-items: center;
999-
min-height: 5rem;
1000-
padding: var(--space-2) 0;
1001-
gap: var(--space-2);
1022+
min-height: 2.25rem;
1023+
padding: 0;
1024+
gap: 0;
10021025
}
10031026

10041027
.tool-sidebar__head:hover {
@@ -1013,11 +1036,11 @@ textarea:focus-visible {
10131036

10141037
.tool-sidebar__groups {
10151038
display: grid;
1016-
gap: var(--space-2);
1039+
gap: var(--space-1);
10171040
opacity: 1;
10181041
transition: opacity var(--motion-normal) var(--ease-standard);
1019-
min-width: var(--size-sidebar);
1020-
padding-right: var(--space-3);
1042+
min-width: calc(var(--size-sidebar) - var(--space-4));
1043+
padding-right: 0;
10211044
}
10221045

10231046
.tool-sidebar--collapsed .tool-sidebar__groups {

0 commit comments

Comments
 (0)