Skip to content
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
d340830
feat: rewrite extension on Vue 3 + TypeScript via @crxjs
dary1337 May 31, 2026
d7e093d
feat: add MIT license
dary1337 May 31, 2026
2530dd5
chore: add coderabbit reviewer config
dary1337 May 31, 2026
44ccc71
ci: add unified workflow with rolling draft on master push
dary1337 May 31, 2026
2409c90
chore: add issue templates for bugs, features, theme suggestions
dary1337 May 31, 2026
73e7cfd
style: format use-import per prettier
dary1337 May 31, 2026
9eed467
fix: treat non-OK HTTP responses as CSS load failures
dary1337 May 31, 2026
00c5365
fix: preserve source CSS for edited and local themes on re-save
dary1337 May 31, 2026
4ea656e
perf: persist user settings once per bulk theme update
dary1337 May 31, 2026
afd8269
fix: reject storage operations on chrome.runtime.lastError
dary1337 May 31, 2026
ccb764b
fix: validate legacy themes during storage migration
dary1337 May 31, 2026
fe1c1bb
fix: abort repo index fetch after an 8s timeout
dary1337 May 31, 2026
d5474ee
fix: open suggest link even when clipboard write fails
dary1337 May 31, 2026
26b68c8
fix: reset editor loading state when source load throws
dary1337 May 31, 2026
91ef7d4
refactor: guard ThemeTab with a type guard instead of casts
dary1337 May 31, 2026
8eb02d1
fix: don't nest a button inside the update-banner anchor
dary1337 May 31, 2026
9b8527a
fix: localize the footer open-in-window tooltip
dary1337 May 31, 2026
abfd8a1
fix: guard updateThemes against re-entrant runs
dary1337 May 31, 2026
75a7143
a11y: make tab bar and screenshots keyboard-operable
dary1337 May 31, 2026
1325619
chore: justify or remove load-bearing type casts
dary1337 May 31, 2026
5bedce9
fix: keep edited and local themes when toggled off via a RepoTheme
dary1337 May 31, 2026
89a2b53
chore: justify the ThemeTab string[] cast
dary1337 May 31, 2026
52b5a31
style: drop blank line before border in update-banner
dary1337 May 31, 2026
f2ce89c
fix: point Chat-GPT and Anilibria theme links to their in-repo folders
dary1337 May 31, 2026
6651155
fix: log scheduled theme-update failures instead of swallowing them
dary1337 May 31, 2026
57e805d
fix: drop the mislabeled 48px icon entry from the manifest
dary1337 May 31, 2026
c4726ea
fix: persist a setting before updating the in-memory store
dary1337 May 31, 2026
17541db
fix: handle theme-update rejection in the footer
dary1337 May 31, 2026
8557f9a
fix: always reload after restoring a theme
dary1337 May 31, 2026
4206803
fix: await local theme persistence on creation
dary1337 May 31, 2026
00dd942
fix: re-validate the cached repo index before use
dary1337 May 31, 2026
81c5470
fix: match a theme link by host boundary, not string prefix
dary1337 May 31, 2026
e373399
fix: un-nest the button inside the bad-layout anchor
dary1337 May 31, 2026
b52ab3f
a11y: add accessible names to screenshots and the search toggle
dary1337 May 31, 2026
76f28e1
chore: justify noUncheckedIndexedAccess in the test tsconfig
dary1337 May 31, 2026
e154a76
test: rename misleading freshStorage helper to loadStorage
dary1337 May 31, 2026
969bdc7
fix: point ReCord links to the author's current theme repo
dary1337 May 31, 2026
5a216fc
fix: normalize scheme-less theme links before URL parsing
dary1337 May 31, 2026
27503a3
fix: handle async failures in theme creation and local repo loading
dary1337 May 31, 2026
8911659
feat: read changelog from a version's GitHub release
dary1337 May 31, 2026
9d9e634
feat: show the live release changelog on the About page
dary1337 May 31, 2026
cb778d3
perf: cache release changelog per version to spare the GitHub API
dary1337 May 31, 2026
a5171f2
fix: clone user settings via JSON so a reactive proxy can't break tog…
dary1337 Jun 1, 2026
ba4f3cc
fix: show the update banner only when the release is strictly newer
dary1337 Jun 1, 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
182 changes: 182 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# CodeRabbit review configuration
# Schema: https://coderabbit.ai/docs/configure-coderabbit
#
# House style: terse, opinionated, technical. Reviewers are expected to
# behave like a senior engineer / tech lead — flag concrete problems with
# evidence, name the rule or principle being violated, demand justification
# for deviations. No filler, no "great work" comments, no celebratory tone.

language: en-US
early_access: false

reviews:
profile: assertive
request_changes_workflow: true
high_level_summary: true
high_level_summary_placeholder: '@coderabbitai summary'
poem: false
review_status: true
collapse_walkthrough: false
sequence_diagrams: false

auto_review:
enabled: true
drafts: false
base_branches:
- master
- main

path_filters:
- '!dist/**'
- '!node_modules/**'
- '!coverage/**'
- '!**/*.min.*'
- '!package-lock.json'

path_instructions:
- path: 'src/**/*.{ts,vue}'
instructions: |
You are reviewing as a TS-strict tech lead. Hold this bar:

- Strict TS only. No `any`, no `as` casts without a written reason in a
comment, no `!` non-null assertions, no `// @ts-ignore`. If a cast is
load-bearing (third-party types lying about reality, runtime narrowing
TS can't see), require a one-line comment justifying it.
- Runtime validation at trust boundaries. Anything crossing the wire
(`fetch`, `chrome.storage`, file imports, postMessage) must be parsed
with zod before use. Schema-by-example object literals are not
validation.
- Async control flow uses async/await + try/catch. Reject callback-style
APIs and `.then().catch()` chains in new code. Floating promises must
be either awaited or explicitly `void`-marked.
- No mutable global side effects in module init or store init (no
`window.addEventListener` in `defineStore(() => {...})` without
cleanup). Side effects belong in component lifecycle hooks with
paired teardown.
- No `chrome.*` API call without thinking about the MV3 service-worker
lifecycle (workers go to sleep; listeners must be top-level).
- Names: kebab-case for `.ts`, PascalCase for `.vue`. Variables
camelCase, types PascalCase, constants UPPER_CASE. If the linter
misses a violation, flag it.
- Defensive code that the type system already proves unreachable is
noise — remove it. Defensive code at trust boundaries stays.

- path: 'src/**/*.vue'
instructions: |
Vue 3 expectations:

- `<script setup lang="ts">` only. Options API and `defineComponent({
setup() { return () => ... } })` render-functions are rejected for
anything but genuinely dynamic UI (slot composition, runtime element
choice). Justify deviations.
- `defineProps` and `defineEmits` must use type-based declarations.
- No `v-html` without a comment explaining why the content is trusted
(compile-time constant, sanitized upstream, etc.). Default answer is
"don't".
- All external `<a target="_blank">` links must carry
`rel="noopener noreferrer"`.
- Composables follow the `use-*` kebab-case file convention and return
a plain object — no implicit reactivity leaks through closure.
- Inline styles and ad-hoc magic numbers belong in tokens or scoped
styles, not in templates.

- path: 'src/stores/**'
instructions: |
Pinia setup-style stores only (`defineStore('x', () => ...)`). No
options-API stores in new code — they have worse type inference and
tree-shaking.

State shape must not lie about loading status. Don't initialize an
entity as `{} as User` and call it loaded; either type as
`User | undefined` and narrow, or carry an explicit `loaded` flag.

Actions that touch chrome.storage must round-trip through the
`@/services/storage` wrapper, not call `chrome.storage.local.*`
directly. The wrapper is where migration and type discipline live.

- path: 'src/services/**'
instructions: |
Service modules are framework-free pure logic. Reject any import from
`vue`, `pinia`, or `vue-router` here. If a service needs reactive
state, the store calls it, not the other way around.

Anything that can fail (network, parsing, fs, chrome API) must return
a meaningful failure value (`undefined`, a `Result`-like, or throw an
`Error` subclass) — never swallow silently except with a `console.warn`
that names the operation.

- path: 'src/background/**'
instructions: |
MV3 service worker code. The worker is restarted on demand — any
top-level state will be lost. All listeners (`chrome.*.on*.addListener`)
must be registered synchronously at module top level, not inside
callbacks or after `await`.

Listener callbacks cannot be `async` directly (their return type is
`void`); use a sync wrapper that calls an async IIFE and either awaits
`sendResponse` or returns `true` to keep the channel open.

No persistent in-memory caches that the popup expects to outlive a
navigation event.

- path: 'tests/**'
instructions: |
Tests document behavior, not implementation. Reject tests that:

- Assert internal call counts on private helpers when an observable
outcome would prove the same thing.
- Mock the module under test (`vi.mock('../the-thing-being-tested')`).
- Use `as any` to coerce inputs the production code couldn't possibly
receive — if the type can't reach the function, neither should the
test.

Every behavioral change in `src/services` or `src/background` requires
a test or a written reason it can't be tested.

- path: '{vite,vitest,eslint,tsconfig}*.{js,ts,json}'
instructions: |
Tooling changes are reviewed as carefully as runtime code. Loosening
a lint rule, lowering `tsconfig` strictness, or excluding files from
type-checking needs a one-line rationale in the PR description.
"It was annoying" is not a rationale.

- path: '{manifest.ts,src/manifest.ts}'
instructions: |
Changes to permissions, host_permissions, or content_scripts trigger
Chrome Web Store re-review and may surprise users. Require an
explanation in the PR body for any addition. Removals are fine.

- path: 'repos.json'
instructions: |
`repos.json` is fetched by every installed extension. Validate any
edit against `src/shared/schemas.ts` (`repoIndexSchema`) before
merging. New entries must have working `cssLink` and `repoLink` URLs
(HTTP 200 on a fresh check).

- path: 'Readme.md'
instructions: |
The README has a deliberate casual voice (lowercase, "u", playful
headings). Don't corporate-ify it. Preserve tone unless the author
changes it themselves.

finishing_touches:
docstrings:
enabled: false

tools:
languagetool:
enabled: false # focus reviewer attention on code, not grammar nits
markdownlint:
enabled: true
eslint:
enabled: true
gitleaks:
enabled: true

chat:
auto_reply: true

knowledge_base:
opt_out: false
learnings:
scope: auto
62 changes: 62 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Bug report
description: Something is broken or behaves unexpectedly
labels: [bug]
body:
- type: input
id: theme
attributes:
label: Affected theme
description: Which theme misbehaves? Leave empty if it's a general extension issue.
placeholder: e.g. Chat-GPT Amoled
validations:
required: false

- type: input
id: site
attributes:
label: Site URL
description: Page where the bug shows up.
placeholder: https://chatgpt.com/...
validations:
required: false

- type: dropdown
id: install_source
attributes:
label: How did you install the extension?
options:
- Chrome Web Store
- GitHub release (zip)
- Built from source
validations:
required: true

- type: input
id: version
attributes:
label: Extension version
description: Open the extension popup → About → version. Or chrome://extensions.
placeholder: 1.1.10
validations:
required: true

- type: textarea
id: what_happened
attributes:
label: What happened?
description: Steps to reproduce + what you expected.
placeholder: |
1. Enabled theme X on site Y
2. Reloaded the page
3. Expected: ...
4. Actually: ...
validations:
required: true

- type: textarea
id: screenshot
attributes:
label: Screenshot
description: Drag & drop here, or paste a URL.
validations:
required: false
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Telegram
url: https://t.me/dary1337
about: Quick chat with the author
27 changes: 27 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Feature request
description: Suggest a new capability for the extension itself (not a new theme)
labels: [enhancement]
body:
- type: textarea
id: problem
attributes:
label: What's the problem?
description: What can't you do today, and what would the feature unlock?
validations:
required: true

- type: textarea
id: proposal
attributes:
label: Proposed solution
description: A rough sketch is fine. UI mockup, API shape, anything.
validations:
required: false

- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Existing workarounds, other extensions, why they're not enough.
validations:
required: false
45 changes: 45 additions & 0 deletions .github/ISSUE_TEMPLATE/theme_suggestion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Suggest a theme
description: Propose a new theme for the curated list
labels: [theme-suggestion]
body:
- type: input
id: title
attributes:
label: Theme title
placeholder: Twitter Amoled
validations:
required: true

- type: input
id: site
attributes:
label: Target website
description: Use `*` for global (applies to all sites).
placeholder: https://twitter.com
validations:
required: true

- type: textarea
id: css
attributes:
label: CSS
description: Paste the theme source. You can also use the in-extension "Suggest" button which copies a ready template.
render: css
validations:
required: true

- type: textarea
id: screenshots
attributes:
label: Screenshots (optional)
description: Before/after of the themed site. Drag & drop here.
validations:
required: false

- type: checkboxes
id: license
attributes:
label: License
options:
- label: I agree to publish this CSS under the repository's MIT license.
required: true
Loading
Loading