A calm place to write long-form, and publish it to the open social web. skypress.blog/
0

Configure Feed

Select the types of activity you want to include in your feed.

Rewrite lexicon page copy in plain voice; render backtick spans as <code>

The owned-schema descriptions come from the lexicon JSON and were emitted as
plain text, so their Markdown backtick spans showed literally instead of as
<code>. Add a tested parseInlineCode helper that splits a description into
text/code segments; the page maps code segments to <code> and lets Astro escape
the rest (no raw HTML on the read path).

Also reword the hand-authored prose without em-dashes and drop the lone em-dash
in the lexicon's own description (single-sourced, so it shows on the page too).

+88 -28
+1 -1
lexicons/blog.skypress.content.gutenberg.json
··· 4 4 "defs": { 5 5 "main": { 6 6 "type": "object", 7 - "description": "A WordPress Gutenberg block tree — the canonical content of a SkyPress document. Placed inside the open `content` union of a `site.standard.document`. Readers that don't understand this format should fall back to the document's `textContent`.", 7 + "description": "A WordPress Gutenberg block tree, the canonical content of a SkyPress document. Placed inside the open `content` union of a `site.standard.document`. Readers that don't understand this format should fall back to the document's `textContent`.", 8 8 "required": ["version", "blocks"], 9 9 "properties": { 10 10 "version": {
+26
src/lib/lexicon/inline-code.test.ts
··· 1 + import { describe, expect, it } from 'vitest'; 2 + import { parseInlineCode } from './inline-code'; 3 + 4 + describe( 'parseInlineCode', () => { 5 + it( 'splits a string into text and code segments', () => { 6 + expect( parseInlineCode( 'fall back to `textContent` always' ) ).toEqual( [ 7 + { text: 'fall back to ', code: false }, 8 + { text: 'textContent', code: true }, 9 + { text: ' always', code: false }, 10 + ] ); 11 + } ); 12 + 13 + it( 'handles a code span at the start and end', () => { 14 + expect( parseInlineCode( '`a` and `b`' ) ).toEqual( [ 15 + { text: 'a', code: true }, 16 + { text: ' and ', code: false }, 17 + { text: 'b', code: true }, 18 + ] ); 19 + } ); 20 + 21 + it( 'returns a single text segment when there are no backticks', () => { 22 + expect( parseInlineCode( 'plain text' ) ).toEqual( [ 23 + { text: 'plain text', code: false }, 24 + ] ); 25 + } ); 26 + } );
+33
src/lib/lexicon/inline-code.ts
··· 1 + /** 2 + * Splits a lexicon description into plain-text and inline-code segments. 3 + * 4 + * Schema descriptions (from the lexicon JSON) use Markdown-style `code` spans. Astro 5 + * outputs interpolated strings as plain text, so the backticks would otherwise render 6 + * literally. The page maps these segments to `<code>` for code parts and bare text for 7 + * the rest; Astro escapes the text nodes, so no raw HTML is injected on the read path. 8 + */ 9 + export interface DescSegment { 10 + text: string; 11 + code: boolean; 12 + } 13 + 14 + export function parseInlineCode( text: string ): DescSegment[] { 15 + const segments: DescSegment[] = []; 16 + const span = /`([^`]+)`/g; 17 + let last = 0; 18 + let match: RegExpExecArray | null; 19 + 20 + while ( ( match = span.exec( text ) ) !== null ) { 21 + if ( match.index > last ) { 22 + segments.push( { text: text.slice( last, match.index ), code: false } ); 23 + } 24 + segments.push( { text: match[ 1 ], code: true } ); 25 + last = match.index + match[ 0 ].length; 26 + } 27 + 28 + if ( last < text.length ) { 29 + segments.push( { text: text.slice( last ), code: false } ); 30 + } 31 + 32 + return segments; 33 + }
+28 -27
src/pages/lexicon.astro
··· 7 7 CONTENT_LEXICON_DESCRIPTION, 8 8 contentSchemaFields, 9 9 } from '../lib/lexicon/schema-doc'; 10 + import { parseInlineCode } from '../lib/lexicon/inline-code'; 10 11 11 12 const contentFields = contentSchemaFields(); 12 13 const schemaJson = JSON.stringify( CONTENT_LEXICON, null, 2 ); 13 14 14 - // Hand-authored interop tables (site.standard.* are community-owned — Decision 0005). 15 + // Hand-authored interop tables (site.standard.* are community-owned; see Decision 0005). 15 16 const documentFields = [ 16 - { name: 'site', req: true, value: "the writer's publication at:// URI — links the document to its publication (Bluesky needs this for the high-fidelity link card)" }, 17 + { name: 'site', req: true, value: "your publication's at:// URI; it links the document to its publication, which Bluesky needs for the rich link card" }, 17 18 { name: 'title', req: true, value: 'the document title' }, 18 19 { name: 'publishedAt', req: true, value: 'when it was published (ISO datetime)' }, 19 - { name: 'path', req: false, value: '/<rkey> — the document’s own record key; the canonical URL is the publication url + this path' }, 20 + { name: 'path', req: false, value: "/<rkey>, the document's own record key; the canonical URL is the publication url plus this path" }, 20 21 { name: 'description', req: false, value: 'a short excerpt' }, 21 - { name: 'textContent', req: false, value: 'plain text of the article — what Bluesky indexes for search and reading time, so we always write it' }, 22 - { name: 'content', req: false, value: 'the open content union — where our blog.skypress.content.gutenberg object lives' }, 22 + { name: 'textContent', req: false, value: "plain text of the article; it's what Bluesky indexes for search and reading time, so we always write it" }, 23 + { name: 'content', req: false, value: 'the open content union, where our blog.skypress.content.gutenberg object lives' }, 23 24 { name: 'bskyPostRef', req: false, value: 'a reference back to the companion Bluesky post' }, 24 25 { name: 'updatedAt', req: false, value: 'set when an article is edited' }, 25 26 ]; 26 27 27 28 const publicationFields = [ 28 - { name: 'url', req: true, value: 'https://skypress.blog/@<handle> — your SkyPress home; articles hang off it' }, 29 + { name: 'url', req: true, value: 'https://skypress.blog/@<handle>, your SkyPress home; articles hang off it' }, 29 30 { name: 'name', req: true, value: 'the publication name (defaults from your handle)' }, 30 31 { name: 'description', req: false, value: 'an optional description' }, 31 32 { name: 'icon', req: false, value: 'an optional icon image' }, ··· 33 34 --- 34 35 35 36 <Base 36 - title="The lexicon — how SkyPress stores your writing" 37 + title="The lexicon: how SkyPress stores your writing" 37 38 description="The open AT Protocol records SkyPress writes to your account when you publish: a publication, a document with your block content, and a Bluesky post linking back." 38 39 > 39 40 <header class="lex-masthead"> ··· 46 47 <h1 class="lex-title">Your words live in <em>your</em> account.</h1> 47 48 <p class="lex-lede"> 48 49 When you publish with SkyPress, nothing gets locked inside SkyPress. Your 49 - writing is saved to your own AT&nbsp;Protocol account in open, published 50 - formats &mdash; so any reader on the open social web can understand it, and 51 - you can take it anywhere. This page documents exactly what we write. 50 + writing is saved to your own AT&nbsp;Protocol account, in open formats any 51 + reader on the social web can understand. You can take it anywhere. Here&rsquo;s 52 + exactly what we write, and why. 52 53 </p> 53 54 54 55 <section class="lex-section"> 55 56 <h2>What gets written when you publish</h2> 56 - <p>Publishing creates a few records on your PDS (your account&rsquo;s data store):</p> 57 + <p>Publishing creates a few records on your PDS (the data store behind your account):</p> 57 58 <ul class="lex-records"> 58 59 <li> 59 - <code>site.standard.publication</code> &mdash; your SkyPress home, 60 - created once and reused after that. 60 + <code>site.standard.publication</code>: your SkyPress home. We create 61 + it once, then reuse it. 61 62 </li> 62 63 <li> 63 - <code>site.standard.document</code> &mdash; the article itself: its 64 + <code>site.standard.document</code>: the article itself, with its 64 65 title, a plain-text copy, and your block content. 65 66 </li> 66 67 <li> 67 - <code>app.bsky.feed.post</code> &mdash; a public Bluesky post with a 68 - link back to the article, so people can find it. 68 + <code>app.bsky.feed.post</code>: a public Bluesky post that links back 69 + to the article, so people can find it. 69 70 </li> 70 71 </ul> 71 72 <p class="lex-honest"> 72 73 One thing worth repeating: publishing also creates a public Bluesky post. 73 - SkyPress always tells you before you hit publish. 74 + We&rsquo;ll always remind you before you hit publish&nbsp;:) 74 75 </p> 75 76 </section> 76 77 77 78 <section class="lex-section"> 78 79 <h2>The format we own: <code>{ CONTENT_LEXICON_ID }</code></h2> 79 - <p>{ CONTENT_LEXICON_DESCRIPTION }</p> 80 + <p>{ parseInlineCode( CONTENT_LEXICON_DESCRIPTION ).map( ( s ) => ( s.code ? <code>{ s.text }</code> : s.text ) ) }</p> 80 81 <table class="lex-table"> 81 82 <thead> 82 83 <tr><th scope="col">Field</th><th scope="col">Type</th><th scope="col">Required</th><th scope="col">What it is</th></tr> ··· 87 88 <td><code>{ f.name }</code></td> 88 89 <td>{ f.type }</td> 89 90 <td>{ f.required ? 'yes' : 'optional' }</td> 90 - <td>{ f.description }</td> 91 + <td>{ parseInlineCode( f.description ).map( ( s ) => ( s.code ? <code>{ s.text }</code> : s.text ) ) }</td> 91 92 </tr> 92 93 ) ) } 93 94 </tbody> 94 95 </table> 95 96 <p class="lex-note"> 96 - This is the only lexicon SkyPress owns &mdash; the reverse-DNS of 97 - <code>skypress.blog</code>. A reader that doesn&rsquo;t understand this 98 - format simply falls back to the document&rsquo;s plain-text 99 - <code>textContent</code>, so your article is never unreadable. The schema 100 - in full: 97 + This is the only lexicon SkyPress owns; the name is the reverse-DNS of 98 + <code>skypress.blog</code>. If a reader doesn&rsquo;t understand the 99 + format, it falls back to the document&rsquo;s plain-text 100 + <code>textContent</code>, so your article is never unreadable. Here&rsquo;s 101 + the full schema: 101 102 </p> 102 103 <pre class="lex-code"><code>{ schemaJson }</code></pre> 103 104 </section> ··· 107 108 <p> 108 109 The publication and document metadata reuse the community 109 110 <a href="https://tangled.org/standard.site/lexicons" target="_blank" rel="noopener noreferrer">standard.site</a> 110 - lexicons rather than anything SkyPress-specific &mdash; so your articles are 111 + lexicons rather than anything SkyPress-specific. That way your articles are 111 112 discoverable by other tools and readers that already speak them, not just by 112 113 SkyPress. 113 114 </p> 114 115 115 - <h3><code>site.standard.document</code> &mdash; the article</h3> 116 + <h3><code>site.standard.document</code>, the article</h3> 116 117 <table class="lex-table"> 117 118 <thead> 118 119 <tr><th scope="col">Field</th><th scope="col">Required</th><th scope="col">What SkyPress writes</th></tr> ··· 128 129 </tbody> 129 130 </table> 130 131 131 - <h3><code>site.standard.publication</code> &mdash; your SkyPress home</h3> 132 + <h3><code>site.standard.publication</code>, your SkyPress home</h3> 132 133 <table class="lex-table"> 133 134 <thead> 134 135 <tr><th scope="col">Field</th><th scope="col">Required</th><th scope="col">What SkyPress writes</th></tr>