Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d622760
refactor(ui): migrate devtools panel to shadcn-vue
jevin98 May 14, 2026
db89a02
fix(ui): restore locale toggle and diff styling
jevin98 May 14, 2026
4fa8502
fix(ui): scroll diff table container to top
jevin98 May 14, 2026
72e3b8f
fix(ui): pin locale and theme controls top right
jevin98 May 14, 2026
1d9d435
fix(ui): use locale selector for language control
jevin98 May 14, 2026
9533426
fix(ui): prevent page scrollbar flicker
jevin98 May 14, 2026
95859ec
Merge origin/main into codex/shadcn-vue-ui
jevin98 May 14, 2026
55d61b5
docs: update screenshot
jevin98 May 14, 2026
cea7766
fix(ui): strengthen diff row contrast
jevin98 May 14, 2026
3caa162
fix(ui): highlight second diff value
jevin98 May 14, 2026
0cfc15d
Merge remote-tracking branch 'origin/main' into codex/shadcn-vue-ui
jevin98 May 14, 2026
f24f8b6
fix(ui): open header info on click
jevin98 May 14, 2026
6452ec5
refactor(ui): redesign diff workspace
jevin98 May 14, 2026
cbaf181
fix(ui): remove filter highlight spacing
jevin98 May 15, 2026
25e538d
fix(ui): mark missing diff values on both sides
jevin98 May 15, 2026
fe5fa71
fix(ui): center empty table state
jevin98 May 15, 2026
141b696
feat(ui): remove individual selections
jevin98 May 15, 2026
fc6f291
fix(ui): align header control sizes
jevin98 May 15, 2026
bb45612
refactor(ui): compact selected element headers
jevin98 May 15, 2026
6cf942e
fix(ui): clear diffs after removing synced selection
jevin98 May 15, 2026
f95235f
fix(ui): unify compact control sizing
jevin98 May 15, 2026
dbbf92b
refactor(ui): remove back to top control
jevin98 May 15, 2026
7e7ce32
Merge remote-tracking branch 'origin/main' into codex/shadcn-vue-ui
jevin98 May 15, 2026
f32c914
feat(ui): show logo in panel header
jevin98 May 15, 2026
4ee4ea4
fix(ui): use neutral language switch icon
jevin98 May 15, 2026
4cd1a71
fix(lint): add node import resolver
jevin98 May 15, 2026
ac959ad
fix(ui): default theme to system preference
jevin98 May 15, 2026
4f787d0
fix(ui): keep detail labels on one line
jevin98 May 15, 2026
b0222ac
docs: update screenshot
jevin98 May 15, 2026
ccd898a
fix(i18n): localize missing css values
jevin98 May 15, 2026
0a47bf4
fix(review): address remaining codex feedback
jevin98 May 15, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ stats.html
stats-*.json
.wxt
web-ext.config.ts
.superpowers/
coverage
playwright-report
test-results
Expand Down
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Guidance for coding agents working in this repository.

- Package manager: `pnpm` (`packageManager` is `pnpm@9.14.1`).
- Extension framework: WXT.
- UI: Vue 3 single-file components with Element Plus and Tailwind CSS.
- UI: Vue 3 single-file components with shadcn-vue style components, Reka UI primitives, and Tailwind CSS v4.
- Localization: WXT/browser `i18n` messages under `public/_locales`.
- Language: TypeScript with Vue type checking through `vue-tsc`.
- Formatting and linting: ESLint 9 with `@antfu/eslint-config`; Prettier is intentionally disabled in VS Code.
Expand All @@ -27,7 +27,7 @@ Guidance for coding agents working in this repository.
- `entrypoints/devtools-panel/lang.ts`: typed wrapper around `browser.i18n.getMessage`.
- `public/_locales/en/messages.json`: English extension and panel messages.
- `public/_locales/zh_CN/messages.json`: Simplified Chinese extension and panel messages.
- `assets/main.css`: Tailwind CSS v4 import and small global Element Plus table overrides.
- `assets/main.css`: Tailwind CSS v4 import, shadcn design tokens, theme variables, and global panel styles.
- `public/icon/`: extension icons.
- `.github/renovate.json5`: dependency update policy.

Expand Down Expand Up @@ -69,7 +69,7 @@ There is no dedicated `lint` or `format` script in `package.json`; use ESLint di
- Keep Vue component state and browser interaction logic in composables such as `useDevToolsPanel`.
- Keep pure data helpers in `entrypoints/devtools-panel/utils/`.
- Preserve the existing i18n structure by updating both `public/_locales/en/messages.json` and `public/_locales/zh_CN/messages.json` when adding user-visible strings.
- Use Element Plus components consistently with the existing DevTools panel UI.
- Use local `components/ui/*` shadcn-vue style components for controls.
- Use Tailwind utility classes for layout and small style adjustments.
- The existing code relies on WXT/browser extension globals such as `browser`; do not replace them with unrelated APIs without a compatibility reason.

Expand All @@ -84,7 +84,7 @@ There is no dedicated `lint` or `format` script in `package.json`; use ESLint di
- Computed styles are normalized by `formatStyle` before comparison.
- Selected elements are compared as `left` and `right`; a third selection is ignored until the current selection is cleared.
- Cross-window/tab synchronization is handled by broadcasting through `browser.tabs.sendMessage`.
- Table rows use color classes to distinguish changed and unchanged CSS properties, and clicking a value cell copies `property: value;`.
- Table rows use monochrome emphasis to distinguish changed and unchanged CSS properties, and clicking a value cell copies `property: value;`.

## Pull Request Expectations

Expand Down
101 changes: 95 additions & 6 deletions assets/main.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,100 @@
@import "tailwindcss";

body {
padding: 16px;
overflow-x: hidden;
overflow-y: scroll;
@source "../entrypoints";
@source "../components";
@source "../lib";

@custom-variant dark (&:where(.dark, .dark *));

@theme inline {
--color-background: hsl(var(--background));
--color-foreground: hsl(var(--foreground));
--color-popover: hsl(var(--popover));
--color-popover-foreground: hsl(var(--popover-foreground));
--color-primary: hsl(var(--primary));
--color-primary-foreground: hsl(var(--primary-foreground));
--color-secondary: hsl(var(--secondary));
--color-secondary-foreground: hsl(var(--secondary-foreground));
--color-muted: hsl(var(--muted));
--color-muted-foreground: hsl(var(--muted-foreground));
--color-accent: hsl(var(--accent));
--color-accent-foreground: hsl(var(--accent-foreground));
--color-destructive: hsl(var(--destructive));
--color-destructive-foreground: hsl(var(--destructive-foreground));
--color-border: hsl(var(--border));
--color-input: hsl(var(--input));
--color-ring: hsl(var(--ring));
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
}

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 0% 9%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
}

.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 0% 98%;
--destructive-foreground: 0 0% 9%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
}

* {
@apply border-border;
}

html {
color-scheme: light;
}

html.dark {
color-scheme: dark;
}

body {
@apply bg-background text-foreground antialiased;
min-width: 0;
margin: 0;
padding: 16px;
overflow-y: hidden;
Comment on lines +86 to +91

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset the body margin before hiding overflow

Because browsers give body an 8px default margin, the new fixed-height panel (100vh - 32px) plus 16px top/bottom padding still occupies more than the DevTools pane once overflow-y is hidden here. In normal browser styling this clips the bottom of the sidebar/table instead of allowing it to scroll; reset the body margin or account for it before disabling overflow.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve a vertical scroll path for short panes

When the DevTools pane is docked/resized short enough that the header plus selection/filter cards exceed the available height, the panel content overflows the fixed-height <main> but the body is no longer scrollable here; only the diff table container has overflow-auto, so the lower controls/table can be clipped and unreachable. Please keep a page-level vertical scroll fallback or make the whole panel shell scroll in this constrained-height case.

Useful? React with 👍 / 👎.

}
}

.el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell{
background-color: inherit !important;
@layer utilities {
.css-diff-scrollbar {
scrollbar-color: hsl(var(--muted-foreground) / 0.35) transparent;
scrollbar-width: thin;
}
}
20 changes: 20 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://shadcn-vue.com/schema.json",
"style": "new-york",
"typescript": true,
"tailwind": {
"config": "",
"css": "assets/main.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"composables": "@/composables",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib"
},
"iconLibrary": "lucide"
}
26 changes: 26 additions & 0 deletions components/ui/button/Button.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { Primitive, type PrimitiveProps } from 'reka-ui'
import { cn } from '@/lib/utils'
import { buttonVariants, type ButtonVariants } from '.'

interface Props extends PrimitiveProps {
variant?: ButtonVariants['variant']
size?: ButtonVariants['size']
class?: HTMLAttributes['class']
}

const props = withDefaults(defineProps<Props>(), {
as: 'button',
})
</script>

<template>
<Primitive
:as="props.as"
:as-child="props.asChild"
:class="cn(buttonVariants({ variant: props.variant, size: props.size }), props.class)"
>
<slot />
</Primitive>
</template>
29 changes: 29 additions & 0 deletions components/ui/button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { cva, type VariantProps } from 'class-variance-authority'

export { default as Button } from './Button.vue'

export const buttonVariants = cva(
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
},
size: {
default: 'h-9 px-4 py-2',
sm: 'h-8 rounded-md px-3 text-xs',
icon: 'h-8 w-8',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
},
)

export type ButtonVariants = VariantProps<typeof buttonVariants>
23 changes: 23 additions & 0 deletions components/ui/checkbox/Checkbox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { Check } from 'lucide-vue-next'
import { CheckboxIndicator, CheckboxRoot } from 'reka-ui'
import { cn } from '@/lib/utils'

const props = defineProps<{
class?: HTMLAttributes['class']
}>()

const checked = defineModel<boolean | 'indeterminate'>('checked')
</script>

<template>
<CheckboxRoot
v-model:checked="checked"
:class="cn('peer h-4 w-4 shrink-0 rounded border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground', props.class)"
>
<CheckboxIndicator class="flex items-center justify-center text-current">
<Check class="h-3.5 w-3.5" />
</CheckboxIndicator>
</CheckboxRoot>
</template>
1 change: 1 addition & 0 deletions components/ui/checkbox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Checkbox } from './Checkbox.vue'
17 changes: 17 additions & 0 deletions components/ui/input/Input.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'

const props = defineProps<{
class?: HTMLAttributes['class']
}>()

const modelValue = defineModel<string | number>()
</script>

<template>
<input
v-model="modelValue"
:class="cn('flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50', props.class)"
>
</template>
1 change: 1 addition & 0 deletions components/ui/input/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Input } from './Input.vue'
29 changes: 29 additions & 0 deletions components/ui/popover/PopoverContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { PopoverContent, PopoverPortal } from 'reka-ui'
import { cn } from '@/lib/utils'

const props = withDefaults(defineProps<{
align?: 'center' | 'end' | 'start'
class?: HTMLAttributes['class']
side?: 'bottom' | 'left' | 'right' | 'top'
sideOffset?: number
}>(), {
align: 'center',
side: 'bottom',
sideOffset: 4,
})
</script>

<template>
<PopoverPortal>
<PopoverContent
:align="props.align"
:side="props.side"
:side-offset="props.sideOffset"
:class="cn('z-50 w-64 rounded-md border bg-popover p-3 text-xs leading-5 text-popover-foreground shadow-md outline-none', props.class)"
>
<slot />
</PopoverContent>
</PopoverPortal>
</template>
2 changes: 2 additions & 0 deletions components/ui/popover/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as PopoverContent } from './PopoverContent.vue'
export { PopoverRoot as Popover, PopoverTrigger } from 'reka-ui'
22 changes: 22 additions & 0 deletions components/ui/select/SelectContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { SelectContent, SelectPortal, SelectViewport } from 'reka-ui'
import { cn } from '@/lib/utils'

const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>

<template>
<SelectPortal>
<SelectContent
position="popper"
:class="cn('relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', props.class)"
>
<SelectViewport class="p-1">
<slot />
</SelectViewport>
</SelectContent>
</SelectPortal>
</template>
27 changes: 27 additions & 0 deletions components/ui/select/SelectItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { Check } from 'lucide-vue-next'
import { SelectItem, SelectItemIndicator, SelectItemText } from 'reka-ui'
import { cn } from '@/lib/utils'

const props = defineProps<{
value: string
class?: HTMLAttributes['class']
}>()
</script>

<template>
<SelectItem
:value="props.value"
:class="cn('relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-xs outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', props.class)"
>
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectItemIndicator>
<Check class="h-3.5 w-3.5" />
</SelectItemIndicator>
</span>
<SelectItemText>
<slot />
</SelectItemText>
</SelectItem>
</template>
21 changes: 21 additions & 0 deletions components/ui/select/SelectTrigger.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { ChevronDown } from 'lucide-vue-next'
import { SelectIcon, SelectTrigger } from 'reka-ui'
import { cn } from '@/lib/utils'

const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>

<template>
<SelectTrigger
:class="cn('flex h-8 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-xs shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1', props.class)"
>
<slot />
<SelectIcon as-child>
<ChevronDown class="h-3.5 w-3.5 opacity-50" />
</SelectIcon>
</SelectTrigger>
</template>
9 changes: 9 additions & 0 deletions components/ui/select/SelectValue.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script setup lang="ts">
import { SelectValue } from 'reka-ui'
</script>

<template>
<SelectValue>
<slot />
</SelectValue>
</template>
5 changes: 5 additions & 0 deletions components/ui/select/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as SelectContent } from './SelectContent.vue'
export { default as SelectItem } from './SelectItem.vue'
export { default as SelectTrigger } from './SelectTrigger.vue'
export { default as SelectValue } from './SelectValue.vue'
export { SelectRoot as Select } from 'reka-ui'
Loading
Loading