diff --git a/.gitignore b/.gitignore index b7f98f7..d25b166 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ _export/ .vscode/ .idea/ page_tmp.html + +# Plugin directory ZIP (scripts/pack-plugin.mjs) +dist-plugin/ diff --git a/PRIVACY.md b/PRIVACY.md index 93c2616..807a30b 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -26,14 +26,21 @@ local files only: every write is triggered by a command you ran. no background writes, no opportunistic writes. -- **`.sipcode/` artifacts in your repo** — manifest, receipts (HTML + PNG), stats HTML, badge JSON, score HTML, benchmark HTML. +- **`.sipcode/` artifacts in your repo**: manifest, receipts (HTML + PNG), stats HTML, badge JSON, score HTML, benchmark HTML, and `install-state.json` (when you last ran `rules --install` or `hygiene --install`, and the rules mode, used by `impact`). - **one named sub-block in your `CLAUDE.md`** — between recognizable markers. `sipcode rules --uninstall` reverses it byte-identically. - **optional entries in `~/.claude/settings.json`** — ONLY when you run `sipcode hygiene --install`. never otherwise. `--uninstall` removes them. - **optional hook scripts at `~/.claude/hooks/sipcode-*.mjs`** — same gate. these scripts also run zero-network; the privacy guard test scans them too. +- **the proxy hook: `~/.claude/hooks/sipcode-proxy.mjs` plus its entry in `~/.claude/settings.json`**, only when you run `sipcode proxy --install` or `sipcode init` (skip it with `init --no-proxy`). `sipcode proxy --uninstall` removes it. it runs inside Claude Code before each tool call, reads and writes only local files, and makes no network calls. - **local caches under `~/.sipcode/`**, written by the commands that use them, so repeat runs do not re-read every log: - `~/.sipcode/usage-cache/claude-code.json` and `~/.sipcode/usage-cache/codex.json` (`today`, `forecast`, `limits`, `regress`): for each session log, its size and modified time plus token counts, model names, timestamps, request ids, each Claude Code request's version and entrypoint and whether it followed a tool result, the session's working folder and the paths of files the agent read. never message text, prompts, file contents or command output. - `~/.sipcode/drift/sessions-v3.jsonl` (`drift`): per-session metrics (session id, end time, token counts, cache-hit rate, duplicate-read tokens, and the project folder as a readable encoded name such as `C--Projects-app`). `~/.sipcode/drift/configs.jsonl` keeps the names of your Claude Code MCP servers over time, so drift can say when a change lines up with a regression. an older `~/.sipcode/drift/sessions.jsonl` from before v1.7.0 is no longer used and can be deleted. - - none of these are ever uploaded. to remove them, delete the folder (for example `rm -rf ~/.sipcode/usage-cache ~/.sipcode/drift`). they are rebuilt on the next run; the only cost is a slower first run. + - `~/.sipcode/proxy-reads/.jsonl` (the proxy hook's re-read check, Claude Code only): for each file Claude read in that session, its path, a sha256 hash of its contents, modified time, size, a rough token estimate, the turn and the time it was first read. never the file contents. a `.warmed` marker beside it records that the cache was filled once from the session transcript. + - `~/.sipcode/proxy-stats/proxy-stats-*.jsonl` (the proxy hook, read by `sipcode proxy --stats`): one line per rewritten or deduplicated tool call, with its time, the tool name, which rewriter acted, the estimated tokens saved and an integrity score. never the command output. + - `~/.sipcode/proxy-signals/.jsonl` (the proxy hook, Claude Code only): the search patterns Claude sent to Grep, Glob or a grep command in that session, with the tool name and the time, so a later read can return only the matching symbols. + - `~/.sipcode/install-pending.json`: written only when `sipcode init` runs while a Claude Code session is active; it holds the hook script and settings paths so the next quiet `sipcode` command can finish the proxy install. it is removed once applied. + - `~/.sipcode/benchmark-live/results.jsonl`: written only when you run `sipcode benchmark --vs-rtk --live`; one line per run with the task id, condition, model, token counts, the cost Claude Code reported and the duration. that command runs `claude --print`, which uses your Claude account and the network the way Claude Code always does. + - `~/.sipcode/.star-nudge`: a marker file (its only content is `sipcode-star-nudge/1`) so the one-time "star on GitHub" line after `proxy --stats` is shown only once per machine. + - none of these are ever uploaded. to remove them, delete the folder (for example `rm -rf ~/.sipcode/usage-cache ~/.sipcode/drift`, or all of `~/.sipcode`). they are rebuilt on the next run; the only cost is a slower first run. - **the receipt PNG is copied to your system clipboard** — ONLY when you run `sipcode receipt`, never silently. via the OS's standard clipboard tool, locally. ## What sipcode never does without you running a command @@ -58,6 +65,14 @@ every command file in `src/commands/` also imports a marker constant `ASSERT_NO_ the guarantee covers **sipcode's own code paths**. it does not extend to third-party libraries (chalk, commander, prompts, etc.), to claude code itself, or to your shell / OS / editor. we publish the test and the source so you can re-verify. +two commands start another program that uses the network, and only when you run them: `sipcode update --run` runs `npm i -g sipcode@latest` (npm downloads the new version), and `sipcode benchmark --vs-rtk --live` runs `claude --print`. without `--run`, `sipcode update` only prints the npm command. + +## This website + +the website (anuj7411.github.io/sipcode) is separate from the CLI and the plugin. it is hosted on GitHub Pages and counts page views with Umami Cloud, which does not set cookies and records page-level data such as the page address, referring site, browser, operating system, screen size and language. the page loads fonts from Google Fonts and the Product Hunt badge image from Product Hunt, so those services and GitHub receive your browser's request (including your IP address) under their own privacy policies. nothing the CLI or the plugin does is sent to the website. + +the web version of this document is at https://anuj7411.github.io/sipcode/privacy/. + ## Future telemetry if a cloud feature ever ships, it will be: diff --git a/docs/plugin-directory/LISTING.md b/docs/plugin-directory/LISTING.md new file mode 100644 index 0000000..83dbdc6 --- /dev/null +++ b/docs/plugin-directory/LISTING.md @@ -0,0 +1,131 @@ +# Sipcode: plugin directory listing kit + +Everything the OpenAI plugin directory (shared by ChatGPT and Codex) asks for, filled in. +Most fields are imported from the ZIP's `plugin.json` when you upload it, so you mostly check them instead of typing them. +Source of truth: `plugins/sipcode/plugin.json` (`extensions["com.openai"]`). If a value here and the manifest ever differ, the manifest wins; fix this file. + +Requirements are from [Upload and submit your plugin](https://developers.openai.com/plugins/deploy/submission), [Plugin submission errors](https://developers.openai.com/plugins/deploy/submission-errors), [Submit desktop-only plugins](https://developers.openai.com/plugins/deploy/desktop-only) and the [plugin guidelines](https://developers.openai.com/plugins/plugin-guidelines), read on 2026-10-16. + +## Submission type + +| Question in the portal | Answer | +|---|---| +| With MCP or Skills only | **Skills only** (four skills, no MCP server, no hooks, no apps) | +| Desktop only | Yes. The manifest sets `requires_local_executor: true`, because each skill runs the `sipcode` command on the user's computer. The listing shows a **Desktop only** badge; people install it from the ChatGPT desktop app. | +| Package version | `1.7.5` (same as the npm package) | +| ZIP | `dist-plugin/sipcode-plugin-1.7.5.zip`, built by `node scripts/pack-plugin.mjs` | + +## Listing fields + +| Field (manifest key) | Limit | Value | Length | +|---|---|---|---| +| Display name (`displayName`) | 30 | `Sipcode` | 7 | +| Short description (`shortDescription`) | 30 | `Spend, limits and regressions` | 29 | +| Long description (`longDescription`) | 4,000 | see below | 1,001 | +| Developer name (`developerName`) | 80 | `Anuj Ojha` (the portal replaces it with the name on your verified identity) | 9 | +| Category (`category`) | one of the list | `Developer Tools` | | +| Capabilities (`capabilities`) | 20 items, 120 chars each | `Read` | | +| Website (`websiteURL`) | HTTPS, 1,024 | https://anuj7411.github.io/sipcode/ | | +| Support (`supportURL`) | HTTPS, 1,024 | https://anuj7411.github.io/sipcode/support/ | | +| Privacy policy (`privacyPolicyURL`) | HTTPS, 1,024 | https://anuj7411.github.io/sipcode/privacy/ | | +| Terms of service (`termsOfServiceURL`) | HTTPS, 1,024 | https://anuj7411.github.io/sipcode/terms/ | | +| Brand color (`brandColor`) | `#RRGGBB`, 2:1 against white | `#5B4FCF` (about 6:1 against white) | | +| Brand color, dark (`brandColorDark`) | optional | not set; the portal derives one | | +| Starter prompts (`defaultPrompt`) | 3, 128 chars each, unique, no @mentions | see below | | +| Logo (`logo`) | square, 48 to 4,096 px, 5 MiB | `./assets/logo.png` | | +| Composer icon (`composerIcon`) | same | `./assets/logo.png` | | +| Screenshots (`screenshots`) | not allowed for skills-only | none (see SCREENSHOTS.md) | | + +Valid categories (from `plugin_category_unknown` in the errors reference): Productivity, Creativity, Developer Tools, Business & Operations, Data & Analytics, Communication, Education & Research, Security, Finance, Healthcare, Travel, Entertainment, Other. + +The four listing URLs are optional for a skills-only plugin ("Website, privacy policy, terms, and support URLs are optional for skills-only plugins"), but the guidelines require a published privacy policy and support contact, so all four are set. They go live when the `docs/site` change is deployed to GitHub Pages (merging to `main` runs `.github/workflows/deploy-site.yml`). Check each one opens before you submit. + +### Long description + +Paste exactly (the blank lines are part of it; line breaks are allowed): + +``` +Sipcode answers questions about your own Codex use from the logs Codex already writes on this computer: how much you spent today or this month (estimated at API prices), how close you are to your usage limits and whether a limit may have shrunk, why a session used so many tokens, and whether Codex or Claude Code got worse after an update. Each answer comes from the Sipcode command-line tool (npm package sipcode), which reads the logs locally and makes no network calls. + +Setup: install the tool once with npm i -g sipcode (Node.js 20 or newer; Node.js 22.15 or newer to read the logs Codex compresses). The plugin runs that command on your computer, so it works where Codex can run local commands: Codex in the ChatGPT desktop app and the Codex CLI. + +Limitations: dollar figures are estimates at API prices, not your bill. Usage limits cover your whole account, so Codex use on other devices or in the cloud raises the percent without local tokens, and a possible limit cut is a signal, not proof. +``` + +### Starter prompts + +The directory takes at most three. These are in the manifest: + +1. `How much did I spend on Codex this month?` (41 characters) +2. `How close am I to my Codex usage limit?` (39) +3. `Did Codex get worse after an update?` (36) + +The fourth question does not fit as a starter prompt but works the same way, and the long description names it: + +4. `Why was my last Codex session so expensive?` + +What each one runs: + +| Ask | Skill | Sipcode runs | +|---|---|---| +| How much did I spend on Codex this month? | `sipcode-spend` | `sipcode forecast --agent codex --json` (also `today` and `stats`) | +| How close am I to my Codex usage limit? | `sipcode-limits` | `sipcode limits` | +| Why was my last Codex session so expensive? | `sipcode-why` | `sipcode why --agent codex --json` | +| Did Codex or Claude Code get worse after an update? | `sipcode-regress` | `sipcode regress --bug-report` (this prompt names both tools; a Codex-only question runs `--agent codex`, which takes about a second, and the Claude Code logs are read only when the user asks about Claude Code) | + +## Package identity (root of `plugin.json`) + +| Field | Value | +|---|---| +| `name` | `sipcode` | +| `version` | `1.7.5` | +| `description` | "Ask about your Codex usage in plain words: spend, usage limits, why a session was expensive, and whether Codex or Claude Code got worse after an update. Runs the Sipcode CLI on this computer, which reads your local Codex and Claude Code logs and makes no network calls." (269 characters) | +| `author.name` / `author.url` | `Anuj Ojha` / https://github.com/Anuj7411 | +| `author.email` | not set (optional). Decision for you, see below. | +| `homepage` | https://anuj7411.github.io/sipcode/ | +| `repository` | https://github.com/Anuj7411/sipcode | +| `license` | `MIT` | +| `keywords` | codex, usage, limits, tokens, spend, regression | + +## Logo and icon + +| File | Format | Size | Bytes | Used for | +|---|---|---|---|---| +| `plugins/sipcode/assets/logo.png` | PNG | 180 x 180 (square) | 4,004 | `logo` and `composerIcon` | + +The spec is "square and at least 48 by 48 pixels", at most 4,096 px and 5 MiB, in PNG, JPEG, WebP or SVG. The current file meets it, so no new icon was made. Optional later: a `logoDark` / `composerIconDark` variant, because the black outline is hard to see on a dark background. + +## Skills in the package + +| Skill | What it answers | +|---|---| +| `sipcode-spend` | Spend today, this month, the month-end forecast, or a period | +| `sipcode-limits` | How close you are to the 5-hour, weekly or 30-day limit, and whether a limit may have shrunk | +| `sipcode-why` | Why a session used so many tokens | +| `sipcode-regress` | Whether Codex or Claude Code got worse after an update | + +Each skill: runs one `sipcode` command, never reads the log files itself, never installs anything, asks the user before re-running with read access when the sandbox blocks `~/.codex`, and tells the user `npm i -g sipcode` when the command is missing. + +## Setup required (say this wherever the form allows notes) + +- Install the CLI once: `npm i -g sipcode`. +- Node.js 20 or newer. Node.js 22.15 or newer to read the session logs Codex compresses (`.jsonl.zst`); on an older Node.js they are skipped, with a warning. +- Works where Codex can run local commands: Codex in the ChatGPT desktop app and the Codex CLI. Not on ChatGPT web or mobile (hence Desktop only). +- No account, no sign-in, no API key, so there are no reviewer credentials to enter. + +## Fields left out on purpose + +| Field | Why | +|---|---| +| `review.test_cases`, `review.demo_recording_url` | "Skills-only plugins don't need MCP app review cases or a demo recording." | +| `screenshots` | `screenshot_configuration_excluded`: screenshots need a remote MCP server with custom UI. | +| `onboardingSkill` | Optional. Could later point at a setup skill that checks `sipcode` is installed. | +| `publication.countries` | Omitted: available everywhere the directory is. | +| `publication.release_notes` | Optional; type them in the portal if asked. | +| `test_credentials`, `reviewer_instructions` | Rejected in ZIP metadata. Nothing to sign in to anyway. | + +## Decisions for Anuj + +- **Support email.** The docs do not require an email: the support URL plus GitHub issues is the support contact. If the portal asks for one, use an address you will read, in place of `SUPPORT_EMAIL_TBD`, and optionally add it as `author.email` in both manifests (the test keeps them identical). +- **Desktop only.** Kept on because the skills cannot work without a local shell. If the portal says `requires_local_executor` is not allowed, the desktop-only page says to send your organization ID to an OpenAI representative; see SUBMIT.md for what to do if you do not have one. +- **Read the terms page** (`docs/site/src/pages/terms.astro`). It is plain words, not legal advice. diff --git a/docs/plugin-directory/SCREENSHOTS.md b/docs/plugin-directory/SCREENSHOTS.md new file mode 100644 index 0000000..cc0b589 --- /dev/null +++ b/docs/plugin-directory/SCREENSHOTS.md @@ -0,0 +1,35 @@ +# Screenshots for the Sipcode plugin + +## Do not put screenshots in the ZIP + +For a skills-only plugin the directory does not take screenshots: + +- Upload check `screenshot_configuration_excluded`: "Remove `interface.screenshots`. Screenshots require **With MCP** and a custom UI." ([submission errors](https://developers.openai.com/plugins/deploy/submission-errors)) +- Final submission: "No screenshots, since those require a remote MCP server with custom UI." +- The [plugin guidelines](https://developers.openai.com/plugins/plugin-guidelines) say the directory no longer shows screenshots and to provide example prompts instead. The three starter prompts in the manifest do that job. + +So `plugin.json` has no `screenshots` field, and `scripts/pack-plugin.mjs` refuses to build a ZIP if one is added. + +## Shots worth taking anyway + +For the README, the website, a launch post, or if a reviewer asks how it works. Only you can take these: they show your own Codex app and your own numbers. + +Setup for every shot: + +- `npm i -g sipcode@latest` first, so the output matches the current release. +- The Sipcode plugin installed (README, "Use Sipcode inside Codex"), the ChatGPT desktop app restarted. +- A **new, empty chat** for each shot (a long chat costs more of your limit and clutters the picture). +- Window about 1600 x 1000, light theme, PNG. Crop to the chat; keep the question and the whole answer in frame. +- Before saving, check nothing private is visible: folder names, project names and session titles in the sidebar can name clients. Collapse the sidebar or crop it out. + +| # | Ask, word for word | Must be visible | File name | +|---|---|---|---| +| 1 | `How much did I spend on Codex this month?` | The question, the "ran sipcode" step, the month-to-date and projected month-end amounts, and the line that they are at API prices | `plugin-spend.png` | +| 2 | `How close am I to my Codex usage limit?` | The 5-hour and weekly percents, the reset times in your local time, and the account-wide caveat (other devices and the cloud) | `plugin-limits.png` | +| 3 | `Why was my last Codex session so expensive?` | The session total, the largest requests list, and the output share | `plugin-why.png` | +| 4 | `Did Codex or Claude Code get worse after an update?` | The coverage lines (requests, sessions, dates, versions) and either the findings or "No regressions detected in your logs" | `plugin-regress.png` | +| 5 | (optional) Open **Plugins** in the desktop app | The Sipcode card with its name, short description and icon | `plugin-card.png` | + +If the limits answer says "a possible limit cut", keep the word "possible" and the caveat in the crop. Do not crop a number away from its caveat. + +Store the files under `docs/screenshots/plugin/` if you want them in the repo, never under `plugins/sipcode/` (that folder is the package). diff --git a/docs/plugin-directory/SUBMIT.md b/docs/plugin-directory/SUBMIT.md new file mode 100644 index 0000000..b0c5527 --- /dev/null +++ b/docs/plugin-directory/SUBMIT.md @@ -0,0 +1,79 @@ +# How to submit Sipcode to the plugin directory + +Plain steps, in order. Field values are in [LISTING.md](LISTING.md). The steps follow OpenAI's [Upload and submit your plugin](https://developers.openai.com/plugins/deploy/submission) and [Submit desktop-only plugins](https://developers.openai.com/plugins/deploy/desktop-only) (read on 2026-10-16). If the portal looks different from this, trust the portal. + +## Before you start (once) + +1. **Put the new pages online.** Merge the `plugin-directory` branch into `main`. That runs the "Deploy site to GitHub Pages" workflow. When it is green, open each link and check it loads and shows your name: + - https://anuj7411.github.io/sipcode/ + - https://anuj7411.github.io/sipcode/support/ + - https://anuj7411.github.io/sipcode/privacy/ + - https://anuj7411.github.io/sipcode/terms/ + + The portal checks that "Public URLs must be accessible and identify the same publisher as the submission." +2. **Read the terms and privacy pages yourself.** They are plain words, not legal advice. Change anything you do not agree with before you submit. +3. **Build the ZIP.** In the repo folder, run: + + ```bash + node scripts/pack-plugin.mjs + ``` + + It prints 9 files and writes `dist-plugin/sipcode-plugin-1.7.5.zip` (about 18 KB). It stops with an error if the plugin folder has anything that a skills-only upload rejects. + +## 1. Check your access + +4. Go to https://platform.openai.com and sign in with the account you want to publish under. (Do this yourself; never share the password.) +5. At the top left, pick the **organization** and **project** that will own the plugin. +6. You must be an **organization owner**, or have the **Apps Management Write** permission. Check at https://platform.openai.com/settings/organization/people/roles. On a personal organization you are usually the owner. +7. **Verify your identity.** Open https://platform.openai.com/settings/organization/general and complete **individual verification** (or business verification, if you want a company name shown). The directory shows the name from this verified identity, for example "Anuj Ojha". You cannot submit without it. + +## 2. Upload the ZIP + +8. Open https://platform.openai.com/plugins. +9. Click **Upload new or existing plugin**. If it asks **With MCP** or **Skills only**, choose **Skills only**. +10. Choose your verified **Developer identity**. +11. Click **Upload plugin** and pick `dist-plugin/sipcode-plugin-1.7.5.zip`. +12. You may see warnings you only need to confirm: + - `manifest_normalized`: the portal saves its own copy of the manifest. Check that nothing important changed. + - `developer_name_defaulted`: the developer name comes from your verified identity. That is expected. +13. **If it says `requires_local_executor` is not allowed:** the desktop-only page says to send your organization ID (from https://platform.openai.com/settings/organization/general) to an OpenAI representative to confirm submission access. If you do not have one, ask OpenAI support through https://help.openai.com (include the plugin ID and organization ID, and say it is a skills-only plugin that runs a local CLI and needs the Desktop only flag). Do not just delete the flag: without it the plugin would also be offered in ChatGPT on the web and on phones, where it cannot run. + +## 3. Fix any findings + +14. Open **Metadata & Skills**. Wait for the checks to finish. The safety scans of the four skills can take **up to 2 hours**. +15. If there are **Issues**, click **Copy issues**. Fix them in `plugins/sipcode/`, run `node scripts/pack-plugin.mjs` again, then click **Upload plugin to fix issues** and pick the new ZIP. (You can paste the copied issues into Codex or Claude Code and ask what to change.) +16. Compare every listing field the portal shows with [LISTING.md](LISTING.md): name, short description, long description, category **Developer Tools**, capability **Read**, the four URLs, the three starter prompts, the icon. These come from the ZIP and are read-only in the portal; to change one, edit `plugins/sipcode/plugin.json` and `plugins/sipcode/.codex-plugin/plugin.json` (keep them the same; `npx vitest run tests/plugin` checks this) and upload again. + +## 4. Review details + +17. Open **Review information > Review details**. + - **Test account / credentials:** none. Sipcode has no account and no sign-in. Leave it empty, or say "No sign-in needed." + - **Test cases and demo video:** not needed for a skills-only plugin. + - **Release notes** (if there is a field): `First directory release: four skills (spend, usage limits, why a session was expensive, regressions after an update) that run the local Sipcode command-line tool.` + - **Notes for the reviewer** (only if there is a free-text field): + + ``` + Sipcode is skills only. Each skill runs the open-source Sipcode CLI on the user's computer, so the reviewer needs: Codex in the ChatGPT desktop app (or the Codex CLI), Node.js 20 or newer, and `npm i -g sipcode` once in a terminal. Sipcode reads the Codex logs in ~/.codex and makes no network calls. With no Codex history on the test machine it reports that no sessions were found. Try: "How much did I spend on Codex this month?" + ``` +18. Click **Save details**. + +## 5. Submit + +19. Select the draft and click **Submit for review**. +20. Read each **policy attestation** yourself and only tick what is true. (For Sipcode: no data collected, no restricted data, no ads, no sales, nothing aimed at children.) +21. Follow progress under **Review status** on the Plugins page. The review team writes to you by **email**. + - Only one review can be open per plugin. To change the ZIP during a review, cancel the review first. + - **Rejected:** read the reason in the email, fix it, build a new ZIP and submit again. To appeal, reply to the rejection email. + +## 6. Publish + +22. When it is approved, open the approved version and click **Publish plugin**. Nothing goes live until you click this. +23. Check the listing in the ChatGPT desktop app: open **Plugins**, search for Sipcode, install it, and ask one of the starter prompts in a new chat. + +## Shipping a new version later + +1. Release the CLI as usual. `plugins/sipcode/plugin.json` and the overlay must carry the same version as `package.json` (a test enforces it, and the portal warns `plugin_version_unchanged` if you reuse a version). +2. Keep the plugin name `sipcode`; an update with a different name is rejected (`plugin_name_mismatch`). +3. Run `node scripts/pack-plugin.mjs`. +4. At https://platform.openai.com/plugins open Sipcode, click **Upload plugin to make changes**, pick the new ZIP, and repeat steps 14 to 22. Each upload gets its own checks and review. Publishing replaces the previous version. +5. A new skill or a changed listing needs a new ZIP. A fix that only changes the `sipcode` npm package does not: users get it with `npm i -g sipcode@latest`. diff --git a/docs/site/src/components/DocPage.astro b/docs/site/src/components/DocPage.astro new file mode 100644 index 0000000..844f3bd --- /dev/null +++ b/docs/site/src/components/DocPage.astro @@ -0,0 +1,167 @@ +--- +// DocPage: shared shell for the plain text pages (terms, privacy, support). +// Same head, NavBar, Footer and type scale as compare.astro; the page body +// goes in the default slot. No MobileNotice modal: these pages must be readable +// on a phone without a dialog in the way. +import "../styles/global.css"; +import NavBar from "./NavBar.astro"; +import Footer from "./Footer.astro"; + +interface Props { + title: string; + description: string; + path: string; // e.g. "terms/" + heading: string; + updated: string; // yyyy-mm-dd +} + +const { title, description, path, heading, updated } = Astro.props; +const base = import.meta.env.BASE_URL; +const canonical = `https://anuj7411.github.io/sipcode/${path}`; +--- + + + + + + {title} + + + + + + + + + + + + + + +
+ + +
+
+

{heading}

+

Last updated: {updated}

+
+
+ +
+
+ + +
+
+ +
+
+ + + + diff --git a/docs/site/src/components/Footer.astro b/docs/site/src/components/Footer.astro index de50bca..23c80f4 100644 --- a/docs/site/src/components/Footer.astro +++ b/docs/site/src/components/Footer.astro @@ -1,6 +1,7 @@ --- // Footer — dark ink ground. Three columns max. No newsletter form, no six // columns, no social icon row for platforms we don't post on. +const base = import.meta.env.BASE_URL; ---