Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI

on:
push:
branches: [master, main]
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
validate:
name: Lint, typecheck, test, build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Typecheck
run: npm run typecheck

- name: Lint
run: npm run lint

- name: Format check
run: npm run format:check

- name: Test
run: npm run test -- --reporter=default

- name: Build
run: npm run build

- name: Upload extension artifact
uses: actions/upload-artifact@v4
with:
name: clay-slip-extension
path: dist
retention-days: 14
66 changes: 7 additions & 59 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,61 +1,9 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
node_modules
dist
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.DS_Store
*.log
.vite
.env

# next.js build output
.next
.env.local
.eslintcache
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dist
coverage
node_modules
*.png
*.jpg
*.svg
package-lock.json
10 changes: 10 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"arrowParens": "always",
"endOfLine": "lf"
}
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018 Clay
Copyright (c) 2026 Clay

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
184 changes: 169 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,173 @@
Slip
====
# Clay Slip

*slip (n): A slip is a liquid mixture or slurry of clay and/or other materials suspended in water used in making pottery*
> A modern Chrome extension for exploring [Clay](https://github.com/clay) CMS pages.

Slip is a Chrome extension for exploring [Clay](https://github.com/clay) pages
Clay annotates rendered HTML with `data-uri` attributes on every component, page, and layout. Clay Slip reads those attributes and gives you a powerful developer overlay β€” visualize component boundaries, inspect data, jump between published and draft versions, and copy URIs without ever opening DevTools.

- adds an info window to the bottom right corner of the page for easy page/layout/component discovery
- adds debugging borders to components
- shift+click opens the (unresolved) instance data in a new tab
- alt+click opens the resolved component instance data in a new tab
- clicking a component highlights it
![Clay Slip panel inspecting a page](docs/screenshots/inspect.png)

## In Progress: Vim-like keystroke functionality
Currently...
- keystroke 'y + c' copies uri of selected component to keyboard
- keystroke 'y + p' copies page uri to keyboard
- keystroke 'o + p' opens the page uri in a new tab
- keystroke 'o + c' opens selected component uri into a new tab (or page uri if no component selected)
## Highlights

- **Manifest V3** Chrome extension built with TypeScript, React, Vite, and `@crxjs/vite-plugin`
- **Shadow-DOM panel** that never collides with host page styles
- **Component tree + find-on-page** β€” live filter dims non-matches on the page, <kbd>Enter</kbd> cycles through them, <kbd>Esc</kbd> clears
- **Inline JSON preview** so you don't need to open a new tab to read component data
- **Diff view** comparing the published version against the unpublished draft, **or** the same URI across two environments
- **Environment switcher** that rewrites every link and JSON fetch through your configured local / dev / staging / prod host
- **Site host mappings** β€” per-brand hostname config that powers a one-click _View on prod / staging / qa_ pill row and lets the Share button hand out cross-env links without leaving the page
- **Open in Clay editor** β€” jump from the page or any component straight into Clay edit mode (always opens the unpublished version)
- **Sticky-note annotations** pinned to component URIs, surfaced as a dot on the page and a dedicated Notes tab β€” leave async review notes for teammates
- **Page audit export** to clipboard as JSON, CSV, or Markdown β€” every component on the page, ready to paste into a ticket or QA checklist
- **Shareable selection links** β€” copy a `?clay-slip-select=…` URL that auto-opens the panel and selects the same component on someone else's machine
- **Component screenshot to clipboard** β€” one-click PNG of any selected component, panel auto-hides during capture
- **SEO tab** β€” title / meta / og / twitter / JSON-LD with a Twitter + Facebook card preview and lints (length, missing image, duplicate `<h1>`, etc.)
- **Recently viewed components** persisted across sessions, with one-click jump back
- **Resizable + dockable panel** β€” drag the inner edges (or the inner-corner grabber) to resize width _and_ height; choose any of four corners or a full-height left/right side dock
- **Toggleable component outlines** (button in the header, <kbd>h</kbd> shortcut) with a configurable opacity
- **Auto / light / dark themes** that respond to OS theme changes live
- **Keyboard shortcuts** with a <kbd>?</kbd> overlay listing every binding
- **Options page** for theme, dock side + width, environment hosts, highlight intensity, shortcut toggle, and recents history size
- **Floating Clay button (FAB)** on every Clay page β€” collapsed/idle state of the panel is a small circular button anchored to the user's preferred corner, with a live component-count badge. Click it to expand the full panel. The standard browser-extension chrome pattern (Sentry / Hotjar / Crisp / Intercom).
- **Smart popup**: friendly "Not a Clay page" popup on non-Clay pages; on Clay pages the toolbar icon mounts/unmounts the entire extension as an escape hatch
- **Toolbar badge** shows the count of Clay components on the current page (cleared on navigation)
- **Click-through-aware selection**: the panel selects the component you clicked but lets real interactive elements (links, buttons, inputs) keep working
- **Copy-as menu**: URI / cURL / `fetch()` snippet / CSS selector β€” all env-host aware
- **Vitest** unit tests and **GitHub Actions** CI on every PR

## Install (development)

```bash
npm install
npm run build
```

Then in Chrome:

1. Visit `chrome://extensions`
2. Enable **Developer mode** (top right)
3. Click **Load unpacked** and select the `dist/` directory

For live development with HMR:

```bash
npm run dev
```

Reload the extension in `chrome://extensions` after switching between `dev` and `build` outputs.

## Usage

| Action | Shortcut / Click |
| ------------------------ | ------------------------------------------------------------------------------------------------- |
| Open the panel | Click the floating **Clay** button (FAB) anchored at your preferred corner |
| Collapse to FAB | Click the collapse button in the panel header (or press <kbd>[</kbd>) |
| Hide the extension | Click the toolbar icon (toggles mount on/off for the current tab) |
| Select a component | Click any outlined element on the page |
| Open in Clay editor | **Edit** button on a page or component β€” opens the page with `?edit=true` |
| Open component JSON | Use the **Data** / **.json** / **.html** buttons in the panel |
| Cross-env diff | **Diff** tab β†’ `Compare:` select β†’ pick another configured env |
| View page on another env | **View on:** pill row in PageInfo (one pill per env configured for this site) |
| Annotate a component | **Inspect** tab, scroll to **Note**, type and Save β€” orange dot appears on the page |
| Share a selection | **Share** button copies for the current env; click **β–Ύ** to share for prod / staging / qa instead |
| Screenshot a component | **Screenshot** button β€” PNG copied to clipboard |
| Export page manifest | **Export β–Ύ** button on the Inspect tab β€” copies JSON / CSV / Markdown to your clipboard |
| Find on page | **Tree** tab search box β†’ matches dim non-matches; <kbd>Enter</kbd> cycles, <kbd>Esc</kbd> clears |
| Resize the panel | Drag the inner vertical / horizontal edge β€” or the inner-corner grabber for both at once |
| Copy URI | Press <kbd>y</kbd> then <kbd>c</kbd> (component) or <kbd>p</kbd> (page) |
| Open URI in new tab | Press <kbd>o</kbd> then <kbd>c</kbd> or <kbd>p</kbd> |
| Toggle outlines on page | Press <kbd>h</kbd> or click the eye icon in the header |
| Cycle environment | Click the `env: …` pill at the bottom of the Inspect tab |
| Show shortcut overlay | Press <kbd>?</kbd> |
| Toggle FAB ↔ panel | Press <kbd>[</kbd> or click the collapse button / the FAB |
| Switch tabs | Press <kbd>i</kbd> (Inspect) or <kbd>t</kbd> (Tree) |
| Open settings | Click the gear icon in the panel header |

## Screenshots

| Inspect | Tree | Options |
| ---------------------------------------- | ---------------------------------- | ---------------------------------------- |
| ![Inspect](docs/screenshots/inspect.png) | ![Tree](docs/screenshots/tree.png) | ![Options](docs/screenshots/options.png) |

## Configuration

### Site host mappings

The **Site host mappings** section in the options page is a per-instance lookup table mapping each brand to its hostnames per environment. With it configured, the panel renders a **View on:** pill row on every Clay page so you can jump to the equivalent URL on a different env, and the **Share** button gains a **β–Ύ** picker for cross-env share links. Empty by default β€” every fork populates its own.

Example for a Vox-Media-style multi-brand setup:

| Label | Production | Staging | QA |
| ------- | --------------- | --------------- | ------------- |
| The Cut | www.thecut.com | stg.thecut.com | qa.thecut.com |
| Vulture | www.vulture.com | stg.vulture.com | |
| Curbed | www.curbed.com | stg.curbed.com | qa.curbed.com |

Hostnames are matched **exactly** (case-insensitive) β€” no prefix stripping or wildcards β€” so the mapping does what you wrote and nothing more. The `dev` and `local` envs are out of scope for site mappings; use the existing **Environments** section for Clay-API hosts.

## Architecture

```
src/
β”œβ”€β”€ manifest.ts # MV3 manifest defined in TypeScript
β”œβ”€β”€ background/
β”‚ └── service-worker.ts # MV3 service worker (open tabs, badge counts)
β”œβ”€β”€ content/
β”‚ β”œβ”€β”€ index.ts # Content script entry
β”‚ β”œβ”€β”€ highlighter.ts # Component outline styles (host DOM)
β”‚ β”œβ”€β”€ shadow-host.ts # Mounts React app inside a Shadow DOM
β”‚ β”œβ”€β”€ page-info.ts # Reads Clay metadata from the page
β”‚ └── panel/ # The React panel UI
β”‚ β”œβ”€β”€ App.tsx
β”‚ β”œβ”€β”€ store.ts # Zustand store
β”‚ β”œβ”€β”€ theme.ts # Light / dark tokens
β”‚ β”œβ”€β”€ styles.css # Shadow-scoped styles
β”‚ β”œβ”€β”€ components/ # Tabs, tree, JSON viewer, diff, breadcrumb…
β”‚ └── hooks/ # Drag, theme, shortcuts, selection
β”œβ”€β”€ popup/ # "Not a Clay page" popup (active until a page sends CLAY_DETECTED)
β”œβ”€β”€ options/ # Full options page (env hosts, dock + width, intensity, recents, shortcuts)
└── lib/ # Pure utilities
β”œβ”€β”€ clay-uri.ts # URI parsing + buildUrl/buildEditorUrl/buildShareLink + copy-as helpers
β”œβ”€β”€ clipboard.ts # Modern + legacy clipboard
β”œβ”€β”€ storage.ts # User preferences in chrome.storage.sync
β”œβ”€β”€ annotations.ts # Sticky notes per component URI
β”œβ”€β”€ recents.ts # Recently viewed components history
β”œβ”€β”€ exporter.ts # Page manifest β†’ JSON / CSV / Markdown
β”œβ”€β”€ seo.ts # Document head extractor + linter
β”œβ”€β”€ screenshot.ts # captureVisibleTab + canvas crop β†’ clipboard PNG
β”œβ”€β”€ site-host.ts # Per-brand hostname mapping β†’ cross-env URL rewrites
└── types.ts # Shared types + DEFAULT_PREFERENCES

```

## Scripts

| Command | What it does |
| ----------------------- | --------------------------------------- |
| `npm run dev` | Vite dev server with HMR |
| `npm run build` | Typecheck + production build β†’ `dist/` |
| `npm run lint` | ESLint with zero-warning policy |
| `npm run lint:fix` | ESLint auto-fix |
| `npm run format` | Prettier write |
| `npm run format:check` | Prettier check (used in CI) |
| `npm run test` | Run Vitest |
| `npm run test:watch` | Vitest in watch mode |
| `npm run test:coverage` | Vitest with coverage |
| `npm run typecheck` | `tsc --noEmit` |
| `npm run validate` | Typecheck + lint + format check + tests |

## Migration notes (1.0 β†’ 2.0)

This release is a full rewrite. There are no breaking _features_ β€” every capability of 1.0 is still present, plus a much larger set of new ones β€” but every implementation file changed:

- **Node 24 LTS** (`.nvmrc` pinned, `engines.node = ">=24"`).
- **Manifest V2 β†’ V3**: replaces `browserAction` and the persistent background page with `action` and a service worker.
- **Vanilla JS β†’ TypeScript 6 + React 19**: the panel UI is React inside a Shadow DOM, with strict typing.
- **Build system**: `npm` + **Vite 8** + `@crxjs/vite-plugin` for HMR-friendly extension development.
- **State**: **Zustand 5** for the panel store.
- **Testing**: **Vitest 4** + happy-dom 20; 85 tests.
- **Lint / format**: ESLint 9 flat config + `typescript-eslint@8` + Prettier 3.
- **CI**: GitHub Actions runs typecheck, lint, format check, tests, and a production build on every push and PR.

## License

MIT β€” see [LICENSE](LICENSE).
21 changes: 0 additions & 21 deletions background.js

This file was deleted.

Binary file added docs/screenshots/inspect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/options.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading