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
59 changes: 38 additions & 21 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ name: Release
# What it does:
# 1. Verifies the tag matches `package.json`'s version (fails fast otherwise).
# 2. Runs the full validation suite (typecheck / lint / format / test).
# 3. Builds the extension into `dist/`.
# 4. Zips `dist/` as `clay-slip-vX.Y.Z.zip` with `manifest.json` at the
# root (so users can drop the unzipped folder straight into Chrome's
# "Load unpacked"; the same layout the Chrome Web Store would expect
# if we ever published there).
# 5. Creates a *draft* GitHub release with the zip attached and auto-
# 3. Builds the extension twice — once for Chromium-family browsers
# (Chrome / Edge / Brave / Arc / Vivaldi / Opera) into `dist/`, and
# once for Firefox into `dist-firefox/` — and zips each as
# `clay-slip-vX.Y.Z.zip` (Chromium) and
# `clay-slip-vX.Y.Z-firefox.zip` with `manifest.json` at the root
# so users can sideload directly ("Load unpacked" on Chromium,
# "Load Temporary Add-on" on Firefox).
# 4. Creates a *draft* GitHub release with both zips attached and auto-
# generated release notes. A human reviews and publishes it.

on:
Expand Down Expand Up @@ -58,40 +60,55 @@ jobs:
echo "::error title=Version mismatch::Tag $TAG does not match package.json version $PKG_VERSION. Bump package.json and re-tag."
exit 1
fi
ZIP="clay-slip-${TAG}.zip"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "zip=$ZIP" >> "$GITHUB_OUTPUT"
ZIP_CHROMIUM="clay-slip-${TAG}.zip"
ZIP_FIREFOX="clay-slip-${TAG}-firefox.zip"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "zip_chromium=$ZIP_CHROMIUM" >> "$GITHUB_OUTPUT"
echo "zip_firefox=$ZIP_FIREFOX" >> "$GITHUB_OUTPUT"

- name: Install dependencies
run: npm ci

- name: Validate (typecheck / lint / format / test)
run: npm run validate

- name: Build
# We use the project's own zip script for both targets so the
# manifest-at-root check + sourcemap stripping run in CI exactly
# as they do locally with `npm run release:dry[:firefox]`.
- name: Build Chromium extension
run: npm run build

- name: Package extension
run: |
cd dist
zip -r "../${{ steps.meta.outputs.zip }}" .
cd ..
ls -lh "${{ steps.meta.outputs.zip }}"
- name: Package Chromium extension
run: npm run zip

- name: Build Firefox extension
run: npm run build:firefox

- name: Package Firefox extension
run: npm run zip:firefox

- name: Upload Chromium zip as workflow artifact
uses: actions/upload-artifact@v4
with:
name: ${{ steps.meta.outputs.zip_chromium }}
path: ${{ steps.meta.outputs.zip_chromium }}
retention-days: 30

- name: Upload zip as workflow artifact
- name: Upload Firefox zip as workflow artifact
uses: actions/upload-artifact@v4
with:
name: ${{ steps.meta.outputs.zip }}
path: ${{ steps.meta.outputs.zip }}
name: ${{ steps.meta.outputs.zip_firefox }}
path: ${{ steps.meta.outputs.zip_firefox }}
retention-days: 30

- name: Create draft GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ steps.meta.outputs.tag }}" \
"${{ steps.meta.outputs.zip }}" \
"${{ steps.meta.outputs.zip_chromium }}" \
"${{ steps.meta.outputs.zip_firefox }}" \
--draft \
--generate-notes \
--title "Clay Slip ${{ steps.meta.outputs.tag }}"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
node_modules
dist
dist-firefox
coverage
.DS_Store
*.log
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dist
dist-firefox
coverage
node_modules
*.png
Expand Down
28 changes: 14 additions & 14 deletions PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ This document is the canonical privacy disclosure for the extension. It's distri
- **No remote code.** All JavaScript is bundled at build time. The extension never loads scripts from the network.
- **No accounts.** The extension does not have any concept of "user" or "session"; nothing is signed in.
- **No third-party endpoints.** The only network calls the extension makes are to the same Clay site you are already viewing.
- **No data leaves your device.** Preferences and notes are stored using `chrome.storage`, which keeps them on your machine (or, for `sync` storage, in your own Google account). The Clay Slip developers never see them.
- **No data leaves your device.** Preferences and notes are stored using the standard WebExtension `storage` APIs — `chrome.storage` on Chromium, `browser.storage` on Firefox — which keep them on your machine (or, for `sync` storage on Chrome, in your own Google account; on Firefox, in your own Mozilla account if Sync is enabled). The Clay Slip developers never see them.

---

## What the extension stores locally

Clay Slip uses the standard Chrome storage APIs. Stored data never leaves the user's device or Google account.
Clay Slip uses the standard WebExtension storage APIs (`chrome.storage` on Chromium, `browser.storage` on Firefox — same shape, same data, same guarantees). Stored data never leaves the user's device or browser-vendor account.

| Storage area | Contents | Why |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `chrome.storage.sync` | UI preferences (theme, panel position/size, site host mappings, highlight mode + intensity, shortcut toggle) | Carries your settings across browsers when you're signed in to Chrome. |
| `chrome.storage.local` | Sticky-note annotations pinned to component URIs; "recently viewed components" history (capped, configurable) | Keeps notes and history available offline; not synced because they may include page-specific context. |
| Storage area | Contents | Why |
| --------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `storage.sync` | UI preferences (theme, panel position/size, site host mappings, highlight mode + intensity, shortcut toggle) | Carries your settings across browsers when you're signed in to Chrome / Firefox Sync. |
| `storage.local` | Sticky-note annotations pinned to component URIs; "recently viewed components" history (capped, configurable) | Keeps notes and history available offline; not synced because they may include page-specific context. |

You can clear everything from the extension's **Options** page (Reset preferences, Clear history) or via Chrome → _Manage extensions_ → _Site access / storage_.
You can clear everything from the extension's **Options** page (Reset preferences, Clear history) or via your browser's _Manage extensions_ → _Site access / storage_ controls (Chromium) or `about:addons` → Clay Slip → _Remove_ (Firefox).

---

Expand Down Expand Up @@ -59,12 +59,12 @@ All of these requests target the Clay site you are already browsing (or another

## Permissions and why each is requested

| Permission | Why it's requested |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `activeTab` | Used by the Screenshot feature: when you click _Screenshot_ on a selected component, the service worker calls `chrome.tabs.captureVisibleTab` and crops the result to the component's bounding box. The PNG is written to your clipboard and discarded — never uploaded. |
| `storage` | Persists the user-controlled state described in the table above. Local-only. |
| `clipboardWrite` | Implements the panel's _Copy URI_, _Copy as cURL/fetch()/CSS_, _Share_, _Export_, and _Screenshot_ actions. Each clipboard write is initiated by an explicit user click. |
| `<all_urls>` | The content script must run on every page so it can detect Clay-rendered pages by reading the `data-uri` attribute on `<html>`. On non-Clay pages the extension exits immediately without reading or modifying anything else. |
| Permission | Why it's requested |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `activeTab` | Used by the Screenshot feature: when you click _Screenshot_ on a selected component, the service worker calls `tabs.captureVisibleTab` (the cross-browser equivalent — `chrome.tabs.captureVisibleTab` on Chromium, `browser.tabs.captureVisibleTab` on Firefox) and crops the result to the component's bounding box. The PNG is written to your clipboard and discarded — never uploaded. |
| `storage` | Persists the user-controlled state described in the table above. Local-only. |
| `clipboardWrite` | Implements the panel's _Copy URI_, _Copy as cURL/fetch()/CSS_, _Share_, _Export_, and _Screenshot_ actions. Each clipboard write is initiated by an explicit user click. |
| `<all_urls>` | The content script must run on every page so it can detect Clay-rendered pages by reading the `data-uri` attribute on `<html>`. On non-Clay pages the extension exits immediately without reading or modifying anything else. |

The extension does **not** request `cookies`, `webRequest`, `webNavigation`, `history`, `bookmarks`, `identity`, `notifications`, `geolocation`, or any other sensitive permission.

Expand All @@ -74,7 +74,7 @@ The extension does **not** request `cookies`, `webRequest`, `webNavigation`, `hi

Clay Slip does **not** execute remote code.

- All JavaScript ships in the `.zip` attached to each [GitHub Release](https://github.com/clay/clay-devtools/releases), bundled at build time by Vite/Rollup. Anyone can verify by checking out the matching `vX.Y.Z` tag and rebuilding with `npm install && npm run build`.
- All JavaScript ships in the `.zip` files attached to each [GitHub Release](https://github.com/clay/clay-devtools/releases) — one for Chromium browsers, one for Firefox — bundled at build time by Vite/Rollup. Anyone can verify by checking out the matching `vX.Y.Z` tag and rebuilding with `npm install && npm run build` (Chromium) or `npm run build:firefox` (Firefox).
- The extension contains no `eval()` or `new Function(string)` calls of remote payloads.
- The extension does not load scripts from any CDN or remote host at runtime.

Expand Down
Loading
Loading