diff --git a/website/STYLE.md b/website/STYLE.md index 5315985..8d02ecb 100644 --- a/website/STYLE.md +++ b/website/STYLE.md @@ -80,8 +80,25 @@ This is planned and not yet available. - Where both modes apply, show the **simple-mode** form and its **advanced-mode (SQL)** equivalent — the in-app teaching echo already pairs these, so docs mirror it. +- Prefer **worked examples** (a real command on the library schema) over + abstract prose, and **always cross-link** the related reference/guide + pages (use stubs so links resolve before a page is written). - Code blocks for exact input/output; reserve casts for motion/flow. +### Code-block fences + +- **Simple-mode commands → ` ```rdbms `** — custom highlight grammar in + `src/grammars/rdbms.mjs`, registered with Expressive Code in + `astro.config.mjs` (keywords + types coloured). +- **Advanced-mode SQL → ` ```sql `**; shell / install → ` ```sh `. +- A decorative `> ` prompt is prepended to `rdbms` lines via CSS + (`src/styles/global.css`) — **do not type `>` in the fence**. It is + copy-safe (Expressive Code's copy button uses `data-code`) and + `user-select:none`. +- **One command per line** in an `rdbms` block; a multi-line single + statement (e.g. advanced `CREATE TABLE`) belongs in a ` ```sql ` block, + where no prompt is added. + ### Canonical library schema (source of truth for examples) Use these exact names/types in every example: diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 5607bc0..9ac3880 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -4,6 +4,10 @@ import starlight from '@astrojs/starlight'; import tailwindcss from '@tailwindcss/vite'; +// Custom highlight grammar for the playground's simple-mode command language +// (advanced-mode examples use the built-in `sql` grammar). Fence with ```rdbms. +import rdbmsLang, { rdbmsSyntax } from './src/grammars/rdbms.mjs'; + // https://astro.build/config export default defineConfig({ // TODO(Phase B/SEO): set `site` to the production URL once the domain is @@ -21,6 +25,8 @@ export default defineConfig({ // public home. Omitted for now rather than linking the wrong repo. // social: [{ icon: 'github', label: 'GitHub', href: '…' }], customCss: ['./src/styles/global.css'], + // Register the simple-mode command grammar with Expressive Code (Shiki). + expressiveCode: { shiki: { langs: [rdbmsLang, rdbmsSyntax] } }, // Pragmatic structure (ADR-0044 §7 / website/STYLE.md): Getting // started, Guides, Reference, Concepts. Autogenerated per directory; // in-section order is controlled by each page's `sidebar.order` diff --git a/website/src/content/docs/concepts/projects-and-storage.md b/website/src/content/docs/concepts/projects-and-storage.md index cd3991a..daefb8f 100644 --- a/website/src/content/docs/concepts/projects-and-storage.md +++ b/website/src/content/docs/concepts/projects-and-storage.md @@ -29,7 +29,7 @@ the CSV files. Because it is derived, it never needs to be shared or backed up separately — and if it is ever missing or stale, the `rebuild` command regenerates it from the readable files: -```text +```rdbms rebuild ``` @@ -46,7 +46,7 @@ When you start the playground without naming a project, it creates a **temporary** project with an automatic name. Temporary projects are perfect for quick experiments. When you want to keep one, give it a name: -```text +```rdbms save ``` @@ -63,7 +63,7 @@ keeping a course's projects together, or for testing. The `export` command packages a project as a zip you can send to anyone: -```text +```rdbms export ``` diff --git a/website/src/content/docs/getting-started/first-project.md b/website/src/content/docs/getting-started/first-project.md index 30562f1..e2e05ec 100644 --- a/website/src/content/docs/getting-started/first-project.md +++ b/website/src/content/docs/getting-started/first-project.md @@ -15,41 +15,41 @@ and press Enter to run them. ## Create a table -A table needs at least a primary key. The `with pk` clause names the -primary-key column and its type: +The quickest way to make a table is `with pk` on its own, which gives you a +ready-made primary key column called `id`: -```text -create table authors with pk author_id(serial) +```rdbms +create table authors with pk ``` -`serial` is an auto-incrementing number — you will not have to fill it in -yourself. +You never fill `id` in yourself — the database assigns it as you add rows. +(You can also name and type the key yourself; see the +[Tables reference](/reference/tables/).) ## Add a couple of columns In simple mode you create a table with its key, then add the other columns one at a time: -```text +```rdbms add column to authors: name (text) add column to authors: birth_year (int) ``` ## Add a row -`insert` adds a row. List the columns you are supplying — the `author_id` -fills itself in because it is a `serial`: +`insert` adds a row. List the columns you are supplying — `id` fills itself +in automatically: -```text +```rdbms insert into authors (name, birth_year) values ('Ada Lovelace', 1815) ``` -The playground shows the row it just inserted, including the generated -`author_id`. +The playground shows the row it just inserted, including the generated `id`. ## Look at the data -```text +```rdbms show data authors ``` diff --git a/website/src/content/docs/getting-started/modes.md b/website/src/content/docs/getting-started/modes.md index 4464d93..91fe1bd 100644 --- a/website/src/content/docs/getting-started/modes.md +++ b/website/src/content/docs/getting-started/modes.md @@ -13,7 +13,7 @@ in **simple mode**, and reach for **advanced mode** when you want full SQL. Simple mode is a friendly, keyword-based command language designed for learning. Commands read close to English: -```text +```rdbms create table authors with pk author_id(serial) show data authors ``` @@ -33,7 +33,7 @@ select title, published from books where published >= 2000 order by published; Switch modes with the `mode` command: -```text +```rdbms mode advanced mode simple ``` @@ -46,7 +46,7 @@ open it, so a project set up for SQL practice reopens in advanced mode. When you are in simple mode and want to run a single SQL statement without switching, prefix the line with a colon: -```text +```rdbms :select count(*) from books ``` diff --git a/website/src/content/docs/guides/build-the-library.md b/website/src/content/docs/guides/build-the-library.md index bc618d1..f79ea1b 100644 --- a/website/src/content/docs/guides/build-the-library.md +++ b/website/src/content/docs/guides/build-the-library.md @@ -15,9 +15,13 @@ from scratch in simple mode: two tables, a relationship between them, and a few rows. By the end you will have used the create → add column → relate → insert → query loop end to end. +We give each table a **named** primary key (like `author_id`) so the +relationship reads clearly — but `with pk` on its own gives you a default +`id` if you prefer, as in [Your first project](/getting-started/first-project/). + ## 1. Create the authors table -```text +```rdbms create table authors with pk author_id(serial) add column to authors: name (text) add column to authors: birth_year (int) @@ -25,7 +29,7 @@ add column to authors: birth_year (int) ## 2. Create the books table -```text +```rdbms create table books with pk book_id(serial) add column to books: title (text) add column to books: author_id (int) @@ -37,7 +41,7 @@ add column to books: published (int) An author has many books, so `books.author_id` should point at `authors.author_id`. Declare that one-to-many relationship: -```text +```rdbms add 1:n relationship from authors.author_id to books.author_id ``` @@ -48,7 +52,7 @@ the `books` side. `author_id` and `book_id` are `serial`, so they fill themselves in: -```text +```rdbms insert into authors (name, birth_year) values ('Ada Lovelace', 1815) insert into authors (name, birth_year) values ('Alan Turing', 1912) insert into books (title, author_id, published) values ('Notes on the Analytical Engine', 1, 1843) @@ -56,7 +60,7 @@ insert into books (title, author_id, published) values ('Notes on the Analytical ## 5. Look at your data -```text +```rdbms show data authors show data books ``` diff --git a/website/src/content/docs/reference/tables.md b/website/src/content/docs/reference/tables.md index c53b771..32049c4 100644 --- a/website/src/content/docs/reference/tables.md +++ b/website/src/content/docs/reference/tables.md @@ -11,16 +11,24 @@ in the Reference. ## Create a table (simple mode) -Every table is created with its primary key. The `with pk` clause lists the -primary-key column(s): +The simplest way to create a table is `with pk` on its own — it gives the +table a ready-made primary-key column named `id` that the database fills in +for you: -```text +```rdbms +create table authors with pk +``` + +Prefer to name the key, or give it a specific type? Name it in the `with pk` +clause: + +```rdbms create table authors with pk author_id(serial) ``` -Other columns are added afterwards with `add column`: +Either way, the rest of the columns are added afterwards with `add column`: -```text +```rdbms add column to authors: name (text) add column to authors: birth_year (int) ``` @@ -30,14 +38,14 @@ add column to authors: birth_year (int) To make the primary key span more than one column, list them, comma separated: -```text +```rdbms create table loans with pk book_id(int), member_id(int) ``` **Syntax** -```text -create table with pk ()[, ...] +```rdbms-syntax +create table with pk [()[, ...]] ``` ## Create a table (advanced mode) @@ -58,13 +66,13 @@ keys — see the Constraints and Relationships references. **Syntax** -```text +```rdbms-syntax create table [if not exists] ( [constraints], ...) ``` ## Drop a table -```text +```rdbms drop table authors ``` diff --git a/website/src/content/docs/reference/types.md b/website/src/content/docs/reference/types.md index 70d8963..d5138d7 100644 --- a/website/src/content/docs/reference/types.md +++ b/website/src/content/docs/reference/types.md @@ -42,7 +42,7 @@ by hand. avoiding easily-confused characters). Always kept unique. Useful when you want an identifier that is compact but not guessable or sequential. -```text +```rdbms create table members with pk member_id(serial) add column to members: member_code (shortid) insert into members (name) values ('Grace Hopper') diff --git a/website/src/grammars/rdbms.mjs b/website/src/grammars/rdbms.mjs new file mode 100644 index 0000000..8231f79 --- /dev/null +++ b/website/src/grammars/rdbms.mjs @@ -0,0 +1,76 @@ +// Minimal Shiki / TextMate grammar for the RDBMS Playground *simple-mode* +// command language, so docs code blocks fenced as ```rdbms get syntax +// highlighting. Advanced-mode examples use the built-in `sql` grammar. +// +// Keyword/clause vocabulary is taken from the in-app help/usage strings +// (`src/friendly/strings/en-US.yaml`) and the command grammar. Matching is +// case-insensitive (keywords are case-insensitive in the app; identifiers +// are case-preserving and intentionally left unscoped so they read as plain +// text against the coloured keywords). +// +// Two language ids share one grammar: +// - `rdbms` — real commands; gets a decorative `> ` prompt via CSS. +// - `rdbms-syntax` — abstract syntax templates (with ); same +// highlighting, but a distinct `data-language` so the +// prompt (scoped to data-language='rdbms') is NOT added. + +const patterns = [ + { include: '#comment' }, + { include: '#string' }, + { include: '#flag' }, + { include: '#number' }, + { include: '#constant' }, + { include: '#type' }, + { include: '#keyword' }, +]; + +const repository = { + comment: { + match: '#.*$', + name: 'comment.line.number-sign.rdbms', + }, + string: { + name: 'string.quoted.single.rdbms', + begin: "'", + end: "'", + patterns: [{ match: "''", name: 'constant.character.escape.rdbms' }], + }, + flag: { + match: '--[A-Za-z][A-Za-z-]*', + name: 'keyword.operator.flag.rdbms', + }, + number: { + match: '\\b[0-9]+(?:\\.[0-9]+)?\\b', + name: 'constant.numeric.rdbms', + }, + constant: { + match: '(?i)\\b(true|false|null)\\b', + name: 'constant.language.rdbms', + }, + type: { + match: '(?i)\\b(text|int|real|decimal|bool|date|datetime|blob|serial|shortid)\\b', + name: 'support.type.rdbms', + }, + keyword: { + match: + '(?i)\\b(create|table|tables|drop|add|column|with|pk|to|from|into|values|insert|update|set|where|delete|show|data|rename|change|alter|relationship|relationships|index|indexes|on|as|references|constraint|not|unique|default|check|primary|key|cascade|restrict|and|or|in|between|like|is|explain|replay|undo|redo|save|new|load|rebuild|export|import|copy|mode|help|hint|quit|messages|all|last|types|simple|advanced)\\b', + name: 'keyword.control.rdbms', + }, +}; + +/** Real simple-mode commands — gets the `> ` prompt (via CSS). */ +export default { + name: 'rdbms', + scopeName: 'source.rdbms', + aliases: ['rdbms-playground'], + patterns, + repository, +}; + +/** Abstract syntax templates — same highlighting, no prompt. */ +export const rdbmsSyntax = { + name: 'rdbms-syntax', + scopeName: 'source.rdbms-syntax', + patterns, + repository, +}; diff --git a/website/src/styles/global.css b/website/src/styles/global.css index c221a37..6eb5983 100644 --- a/website/src/styles/global.css +++ b/website/src/styles/global.css @@ -2,3 +2,26 @@ @import '@astrojs/starlight-tailwind'; @import 'tailwindcss/theme.css' layer(theme); @import 'tailwindcss/utilities.css' layer(utilities); + +/* Command prompt for simple-mode (`rdbms`) code blocks: a decorative "> " + before each command line so consecutive commands read as distinct entered + lines (advanced-mode `sql` blocks are left unprefixed). It is CSS ::before + content with user-select:none, so it is NOT part of the copy buffer + (Expressive Code copies the block's `data-code`) and is never selected. */ +[data-language='rdbms'] .ec-line .code::before { + content: '> '; + color: var(--sl-color-gray-3); + user-select: none; + -webkit-user-select: none; +} + +/* The app's input field is single-line, so a multi-command copy can't be + pasted-and-run as one unit. Hide Expressive Code's copy button on + *multi-line* `rdbms` blocks (each line is a separate command — type them + one at a time). Single-command `rdbms` blocks keep copy, and `sql` / `sh` + blocks are unaffected (always one pasteable unit). Also hide it on + `rdbms-syntax` templates, which contain and aren't runnable. */ +figure:has(> pre[data-language='rdbms'] .ec-line + .ec-line) .copy, +figure:has(> pre[data-language='rdbms-syntax']) .copy { + display: none; +}