Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 6 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
runs-on: ubuntu-latest
env:
RELEASE_ZIP: VidQuery-${{ github.ref_name }}-chrome-unpacked.zip
LATEST_ZIP: VidQuery-chrome-unpacked.zip
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
Expand All @@ -24,19 +25,20 @@ jobs:
- run: pnpm install --frozen-lockfile
- name: Validate tag and extension version
run: |
package_version="$(node -p "require('./package.json').version")"
package_version="$(node -p "require('./apps/extension/package.json').version")"
test "${GITHUB_REF_NAME}" = "v${package_version}"
- run: pnpm release:zip
- name: Prepare unpacked Chrome archive
run: |
mkdir -p release
source_zip="$(find .output -maxdepth 1 -type f -name '*-chrome.zip' -print -quit)"
source_zip="$(find apps/extension/.output -maxdepth 1 -type f -name '*-chrome.zip' -print -quit)"
test -n "${source_zip}"
cp "${source_zip}" "release/${RELEASE_ZIP}"
cp "${source_zip}" "release/${LATEST_ZIP}"
- name: Validate unpacked package layout
run: unzip -Z1 "release/${RELEASE_ZIP}" | grep -Fxq manifest.json
- name: Create checksums
run: sha256sum "${RELEASE_ZIP}" > SHA256SUMS.txt
run: sha256sum "${RELEASE_ZIP}" "${LATEST_ZIP}" > SHA256SUMS.txt
working-directory: release
- uses: softprops/action-gh-release@v2
with:
Expand All @@ -45,4 +47,5 @@ jobs:
fail_on_unmatched_files: true
files: |
release/${{ env.RELEASE_ZIP }}
release/${{ env.LATEST_ZIP }}
release/SHA256SUMS.txt
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,10 @@ yarn-lock.json

# Tarball archives generated by packaging tools (e.g., npm pack); ignore all .tgz files.
*.tgz

# Workspace build and dependency output.
**/node_modules/
**/dist/
**/.output/
**/.wxt/
**/.netlify/
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@
.out/
.output/
.wxt/
**/.output/
**/.wxt/
**/.netlify/
**/dist/
apps/web/public/prototypes/
apps/web/src/routeTree.gen.ts


####################################################
Expand Down
15 changes: 8 additions & 7 deletions DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ pnpm install --frozen-lockfile
pnpm check
```

The Chrome build is written to `.output/chrome-mv3/`.
The Chrome build is written to `apps/extension/.output/chrome-mv3/`.

To test it locally:

1. Open `chrome://extensions`.
2. Enable **Developer mode**.
3. Select **Load unpacked**.
4. Choose `.output/chrome-mv3/`.
4. Choose `apps/extension/.output/chrome-mv3/`.
5. Open the popup, connect Gemini, then test the Side Panel on watch, Shorts, and live-video routes.

## Create the distributable archive
Expand All @@ -25,13 +25,13 @@ To test it locally:
pnpm release:zip
```

`release:zip` runs the complete verification suite before WXT creates a ZIP under `.output/`. Inspect the generated manifest and archive contents before distributing it.
`release:zip` runs the complete verification suite before WXT creates a ZIP under `apps/extension/.output/`. Inspect the generated manifest and archive contents before distributing it.

## Publish a GitHub Release

The tag-triggered [Release workflow](.github/workflows/release.yml) publishes the same unpacked Chrome archive pattern used by the reference extensions.

1. Update the version in `package.json` and ensure the lockfile is current.
1. Update the version in `apps/extension/package.json` and ensure the root lockfile is current.
2. Update [.github/RELEASE_NOTES.md](.github/RELEASE_NOTES.md) for that version.
3. Run `pnpm check` locally.
4. Commit and merge the release-ready changes.
Expand All @@ -42,19 +42,20 @@ The tag-triggered [Release workflow](.github/workflows/release.yml) publishes th
git push origin v2.0.0
```

The workflow rejects a tag that does not equal `v` plus the version in `package.json`. A successful run publishes:
The workflow rejects a tag that does not equal `v` plus the version in `apps/extension/package.json`. A successful run publishes:

- `VidQuery-vX.Y.Z-chrome-unpacked.zip`;
- `VidQuery-chrome-unpacked.zip`, the stable landing-page download target;
- `SHA256SUMS.txt`; and
- the curated release notes from `.github/RELEASE_NOTES.md`.

The archive is validated before publication to ensure `manifest.json` is at its root. GitHub Release installations remain manual and do not update automatically.

## Release checklist

- Confirm `package.json` contains the intended extension version.
- Confirm `apps/extension/package.json` contains the intended extension version.
- Run `pnpm check` from a clean checkout with the committed pnpm lockfile.
- Load `.output/chrome-mv3/` into the minimum supported Chrome version.
- Load `apps/extension/.output/chrome-mv3/` into the minimum supported Chrome version.
- Verify first-time consent, validated and unvalidated key saves, key removal, and legacy-key migration.
- Verify watch, Shorts, live, unsupported-page, transcript-available, and transcript-unavailable states.
- Verify question submission, Markdown rendering, edit, retry, quota, invalid-key, and offline errors.
Expand Down
57 changes: 27 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

> Ask Gemini questions grounded in the YouTube video you are watching, from a focused Chrome Side Panel.

[![CI](https://github.com/montasim/youtube-helper/actions/workflows/ci.yml/badge.svg)](https://github.com/montasim/youtube-helper/actions/workflows/ci.yml)
[![CI](https://github.com/montasim/VidQuery/actions/workflows/ci.yml/badge.svg)](https://github.com/montasim/VidQuery/actions/workflows/ci.yml)
[![Support on SupportKori](https://img.shields.io/badge/Support_on-SupportKori-00B8B5)](https://www.supportkori.com/montasim)

VidQuery is a Chromium extension for people who want to question a video without moving its transcript and metadata into a separate chat tool. Open a YouTube watch, Shorts, or live-video page; the extension collects a bounded Video Context locally and sends it directly to Google Gemini only after you submit a question.

The V2 extension uses a Chrome Side Panel for conversations and a compact toolbar popup for consent and Gemini connection setup. It has no intermediary backend, account, analytics service, or persistent conversation archive.

**[Browse releases](https://github.com/montasim/youtube-helper/releases) · [Preview the approved interface](prototypes/v1/index.html) · [Report an issue](https://github.com/montasim/youtube-helper/issues)**
**[Browse releases](https://github.com/montasim/VidQuery/releases) · [Preview the approved interface](prototypes/v1/index.html) · [Report an issue](https://github.com/montasim/VidQuery/issues)**

## What it does

Expand All @@ -28,7 +28,7 @@ The V2 extension uses a Chrome Side Panel for conversations and a compact toolba

Once a release has been published by the tag workflow:

1. Open [GitHub Releases](https://github.com/montasim/youtube-helper/releases).
1. Open [GitHub Releases](https://github.com/montasim/VidQuery/releases).
2. Download `VidQuery-vX.Y.Z-chrome-unpacked.zip` and `SHA256SUMS.txt`.
3. Place both files in the same folder and verify the download:

Expand All @@ -54,19 +54,19 @@ GitHub-installed builds do not update automatically. Repeat this process for eac
### Build and load

```bash
git clone https://github.com/montasim/youtube-helper.git
cd youtube-helper
git clone https://github.com/montasim/VidQuery.git
cd VidQuery
pnpm install
pnpm build
pnpm build:extension
```

1. Open `chrome://extensions`.
2. Enable **Developer mode**.
3. Select **Load unpacked**.
4. Choose `.output/chrome-mv3/` from this repository.
4. Choose `apps/extension/.output/chrome-mv3/` from this repository.
5. Pin VidQuery to the browser toolbar if desired.

For an installable archive, run `pnpm zip`; WXT writes the packaged extension under `.output/`.
For an installable archive, run `pnpm release:zip`; WXT writes the packaged extension under `apps/extension/.output/`.

## First use

Expand Down Expand Up @@ -121,41 +121,38 @@ flowchart LR
| Session credential cache | Restricted `chrome.storage.session` |
| Device encryption key | Non-exportable Web Crypto key in IndexedDB |
| Tests | Vitest, Testing Library, Happy DOM, fake-indexeddb |
| Product website | TanStack Start, shadcn, Tailwind CSS 4, Netlify |

The project’s domain language is in [CONTEXT.md](CONTEXT.md). Architectural trade-offs are recorded under [docs/adr](docs/adr).

## Development

```bash
pnpm install
pnpm dev
pnpm dev:extension
```

WXT prints the development output path. Load that unpacked directory in Chrome, keep a YouTube video open, and reload the extension after permission or manifest changes.

| Command | Purpose |
| ------------------- | ------------------------------------------------------ |
| `pnpm dev` | Start WXT development mode |
| `pnpm build` | Build the Chrome Manifest V3 extension |
| `pnpm zip` | Build and package the extension |
| `pnpm release:zip` | Run the full quality gate and package the extension |
| `pnpm typecheck` | Check TypeScript without emitting files |
| `pnpm lint` | Run ESLint with zero warnings allowed |
| `pnpm format:check` | Verify Prettier formatting |
| `pnpm test` | Run the Vitest suite once |
| `pnpm check` | Run formatting, linting, type checks, tests, and build |
| Command | Purpose |
| ---------------------- | ------------------------------------------------------- |
| `pnpm dev:extension` | Start WXT extension development |
| `pnpm dev:web` | Start the TanStack Start landing page |
| `pnpm build:extension` | Build the Chrome Manifest V3 extension |
| `pnpm build:web` | Build the Netlify-ready website |
| `pnpm release:zip` | Validate and package the extension |
| `pnpm check:extension` | Run extension formatting, lint, types, tests, and build |
| `pnpm check:web` | Run website formatting, lint, types, and Netlify build |
| `pnpm check` | Run the complete workspace quality gate |

## Project structure

```text
entrypoints/ WXT background, content-script, popup, and Side Panel entry points
src/application/ Application errors and orchestration boundaries
src/domain/ Validated Video Context and retained-data schemas
src/infrastructure/ Gemini adapter and browser storage implementations
src/shared/ Typed browser protocol and shared utilities
src/ui/ Tailwind/shadcn components and React surfaces
tests/ Credential, storage, prompt, and UI verification
prototypes/v1/ Standalone approved interface reference
apps/extension/ WXT extension source, tests, and package configuration
apps/web/ TanStack Start, shadcn, Tailwind, and Netlify website
assets/brand/ Canonical VidQuery brand assets
prototypes/v1/ Standalone approved extension interface reference
prototypes/landing/ Original static landing-page reference
docs/adr/ Accepted architecture decisions
```

Expand All @@ -171,13 +168,13 @@ docs/adr/ Accepted architecture decisions

## Automated releases

Pushing a version tag such as `v2.0.0` runs the [Release workflow](.github/workflows/release.yml). The tag must match `package.json`. The workflow installs the locked dependency graph, runs the complete quality gate, builds the WXT archive, verifies that `manifest.json` is at its root, generates a SHA-256 checksum, and creates a GitHub Release containing both files.
Pushing a version tag such as `v2.0.0` runs the [Release workflow](.github/workflows/release.yml). The tag must match `apps/extension/package.json`. The workflow installs the locked dependency graph, runs the complete quality gate, builds the WXT archive, verifies that `manifest.json` is at its root, generates SHA-256 checksums, and creates a GitHub Release with versioned and stable download filenames.

Release descriptions come from [.github/RELEASE_NOTES.md](.github/RELEASE_NOTES.md) and should be updated before tagging. See [DEPLOYMENT.md](DEPLOYMENT.md) for the maintainer checklist and exact release procedure. The repository does not publish this extension to npm or submit it automatically to the Chrome Web Store.

## Support and security

Use [GitHub Issues](https://github.com/montasim/youtube-helper/issues) for reproducible bugs and feature requests. Include the browser version, YouTube route type, whether a transcript was available, and the visible error message. Never include an API key, transcript, private question, or full Gemini request in an issue.
Use [GitHub Issues](https://github.com/montasim/VidQuery/issues) for reproducible bugs and feature requests. Include the browser version, YouTube route type, whether a transcript was available, and the visible error message. Never include an API key, transcript, private question, or full Gemini request in an issue.

Report vulnerabilities privately using [SECURITY.md](SECURITY.md).

Expand Down
2 changes: 2 additions & 0 deletions apps/extension/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.output/
.wxt/
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
58 changes: 58 additions & 0 deletions apps/extension/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"name": "@vidquery/extension",
"version": "2.0.0",
"private": true,
"type": "module",
"description": "A private-by-default Gemini companion for YouTube videos.",
"scripts": {
"dev": "wxt",
"prepare": "wxt prepare",
"build": "wxt build",
"zip": "wxt zip",
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "eslint . --max-warnings=0",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest",
"check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test && pnpm build",
"release:zip": "pnpm check && wxt zip"
},
"dependencies": {
"@fontsource-variable/instrument-sans": "^5.2.8",
"@fontsource-variable/manrope": "^5.2.8",
"@fontsource/ibm-plex-mono": "^5.2.7",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.544.0",
"radix-ui": "^1.4.3",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"tailwind-merge": "^3.3.1",
"zod": "^4.1.12"
},
"devDependencies": {
"@eslint/js": "^9.39.0",
"@tailwindcss/vite": "^4.1.17",
"@testing-library/jest-dom": "6.9.1",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/chrome": "^0.1.32",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@vitest/coverage-v8": "^4.0.8",
"@wxt-dev/module-react": "^1.2.0",
"eslint": "^9.39.0",
"fake-indexeddb": "^6.2.4",
"globals": "^16.5.0",
"happy-dom": "^20.0.10",
"prettier": "^3.6.2",
"tailwindcss": "^4.1.17",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.3",
"vitest": "^4.0.8",
"wxt": "^0.20.11"
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions apps/web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
.env
.nitro
.tanstack
.wrangler
.output
.vinxi
__unconfig*
todos.json

# Local Netlify folder
.netlify
4 changes: 4 additions & 0 deletions apps/web/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package-lock.json
pnpm-lock.yaml
yarn.lock
src/routeTree.gen.ts
28 changes: 28 additions & 0 deletions apps/web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# VidQuery website

The VidQuery product website is a TanStack Start application using React, Tailwind CSS 4, and shadcn components. Netlify builds this workspace independently from the WXT extension.

## Development

From the repository root:

```bash
pnpm install
pnpm dev:web
```

The local site runs at `http://localhost:3000`.

## Validation

```bash
pnpm check:web
```

This generates the TanStack route tree, checks formatting and linting, runs strict TypeScript validation, and produces the Netlify client and SSR builds.

## Deployment

Connect the repository to Netlify, leave the base directory at the repository root, and set the package directory to `apps/web`. The committed `netlify.toml` builds `@vidquery/web` and publishes `apps/web/dist/client`.

The download CTA uses GitHub’s stable latest-release asset URL. Each extension release must therefore include `VidQuery-chrome-unpacked.zip`.
25 changes: 25 additions & 0 deletions apps/web/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "radix-nova",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/styles.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "#/components",
"utils": "#/lib/utils",
"ui": "#/components/ui",
"lib": "#/lib",
"hooks": "#/hooks"
},
"iconLibrary": "lucide",
"rtl": false,
"menuColor": "default",
"menuAccent": "subtle",
"registries": {}
}
Loading
Loading