<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>技述 — JS.GRIPE</title><link>https://www.js.gripe/</link><description>Independent development, curious experiments and open-source collaboration, with AI as a tool for exploration.</description><language>en</language><lastBuildDate>Fri, 30 Oct 2026 01:04:41 GMT</lastBuildDate><atom:link href="https://www.js.gripe/rss.xml" rel="self" type="application/rss+xml"/><item><title>One firewall, several business origins</title><link>https://www.js.gripe/2026/10/edgeaegis-multiple-origins/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/edgeaegis-multiple-origins/</guid><pubDate>Thu, 29 Oct 2026 00:00:00 GMT</pubDate><dc:date>2026-10-29T00:00:00.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>EdgeAegis runs native OWASP CRS before a request reaches your business. Configure multiple internal functions, VPS origins and load-balancer hostnames in one routing file, with separate credentials for each trust boundar</description><category>edgeaegis</category><content:encoded>&lt;p&gt;EdgeAegis runs native OWASP CRS before a request reaches your business. Configure multiple internal functions, VPS origins and load-balancer hostnames in one routing file, with separate credentials for each trust boundary.&lt;/p&gt;
&lt;h2&gt;Real terminal, real HTTP responses&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://www.js.gripe/images/previews/edgeaegis-routing.3d240e2bb6009766.gif&quot; alt=&quot;EdgeAegis VM terminal&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The isolated VM recording uses curl against two functions and three authenticated HTTPS origins. A SQL injection receives 403 without another origin access; a five-second demonstration ban then expires. The five-second TTL is for this lab, not the production default.&lt;/p&gt;
&lt;h2&gt;Rules and routing stay separate&lt;/h2&gt;
&lt;p&gt;rules/config.yml selects native rules, policy and your allowlist. origin/config.yml maps public hostname/path patterns to destinations. secret/credentials.yml holds independent origin keys in your private deployment repository.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;npm exec --package=edgeaegis -- edgeaegis init my-firewall
cd my-firewall
npm install
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;routes:
  - pattern: api.example/shop/*
    origin: function:shop
  - pattern: api.example/orders/*
    origin: https://192.0.2.10
    key: orders-vps
  - pattern: api.example/search/*
    origin: https://lb.example
    key: search-lb
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Internal functions stay behind inspection&lt;/h2&gt;
&lt;p&gt;A configured function: selects origin///index.js. The shared firewall entry point inspects the request before calling the function, then inspects its response. External origins must verify their own selected key.&lt;/p&gt;
&lt;h2&gt;Measure the boundaries&lt;/h2&gt;
&lt;p&gt;Complete CRS compilation and execution are checked before rule updates. Unknown semantics fail visibly. Resource bounds and platform limits still apply; upgraded WebSocket payloads are opaque to SecLang and Worker-only counters are local to an engine instance.&lt;/p&gt;
&lt;p&gt;Vibe Coding and Agent reports are welcome. Run the relevant tests before submitting code. Sensitive findings need a private report with an affected version, sanitized reproducer, impact and actual verification evidence.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.js.gripe/edgeaegis/&quot; rel=&quot;noopener noreferrer&quot;&gt;Explore EdgeAegis&lt;/a&gt; · &lt;a href=&quot;https://github.com/jsw-teams/edgeaegis#readme&quot; rel=&quot;noopener noreferrer&quot;&gt;README&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Building AMA-1: a clean baseline and two rounds of unseen tests</title><link>https://www.js.gripe/2026/10/ama1-genesis-two-round-review/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/ama1-genesis-two-round-review/</guid><pubDate>Sat, 17 Oct 2026 18:11:40 GMT</pubDate><dc:date>2026-10-17T18:11:40.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>AGY implemented, Codex reviewed and later contributed separately identified patches. All 85 regression tests pass, while unseen routing accuracy remains 37.5% and 33.3%.</description><category>ama</category><category>AMA</category><category>Local AI</category><category>From scratch</category><category>Independent testing</category><content:encoded>&lt;p&gt;I want to explore a local assistant that gradually learns about the world, recognizes its own limits and grows through tools and checked observations. The AMA experiments begin with clean data and random initialization, keep resource use modest and preserve failures alongside successful runs.&lt;/p&gt;
&lt;p&gt;AMA-1 Genesis started from a small v0.1 prototype. Codex launched AGY to implement changes and independently reviewed the work. After two test rounds exposed repairs that covered only known paths, I authorized Codex to contribute separately identified patches. The result is a runnable foundation. The full capability target is still unfinished.&lt;/p&gt;
&lt;h2&gt;The goal and the current system&lt;/h2&gt;
&lt;p&gt;The six directions are training from scratch, a truthful self-model and stable personality, world and object knowledge, reviewed continual learning, lightweight tool use, and trustworthy networking. The intended order is self-model, objects, tools, correction and learning, followed by more natural conversation and multimodal perception.&lt;/p&gt;
&lt;p&gt;The component actually trained here is a &lt;strong&gt;516-parameter linear routing classifier&lt;/strong&gt; using character bigrams. Its 24 synthetic examples distinguish self, world, search and other requests. It is not a language model. Object attributes in SQLite do not constitute visual recognition.&lt;/p&gt;
&lt;p&gt;The personality contract takes inspiration from Kal’tsit’s calm, rational independence while AMA-1 keeps its own identity. Subjective consciousness remains an open research question. Functional state reporting does not establish subjective experience.&lt;/p&gt;
&lt;h2&gt;Implementation and review had separate jobs&lt;/h2&gt;
&lt;p&gt;The original files were inventoried and frozen before a clean Git baseline was saved. No old project code, datasets or model weights were transplanted. Review used source code, actual command output and checkpoint provenance; historical writeups were not accepted as evidence of a new run.&lt;/p&gt;
&lt;p&gt;The initial 16 tests passed. Additional checks found malformed checkpoints being accepted, contradictory knowledge surviving correction, unsafe URL handling and CAPTCHA pages being counted as successful searches. AGY added typed object records, episodic events, a tool registry, candidate manifests and rollback operations, together with tests.&lt;/p&gt;
&lt;p&gt;Those additions had their own gaps. Redefining an object could discard its identity. New property APIs bypassed review. A matching hash could still describe an invalid model. A corrupt backup could replace a valid model. Even a fixed search endpoint could redirect toward a private address. The network reproduction used a mocked transport and made no actual private-network request.&lt;/p&gt;
&lt;h2&gt;How the two corpora were separated&lt;/h2&gt;
&lt;p&gt;Both corpora were frozen and hashed before evaluation, and the implementer was explicitly prohibited from reading them. The original 24 training rows stayed unchanged. Review checked literal and Unicode/case-normalized overlap and inspected visible tool traces.&lt;/p&gt;
&lt;p&gt;This was an implementer-hidden independent evaluation, &lt;strong&gt;not a formal double-blind experiment&lt;/strong&gt;. There was no OS access-control separation, and the reviewer knew the questions. The classification task also shares intent labels and domain semantics across its splits.&lt;/p&gt;
&lt;p&gt;Once first-round feedback guided repairs, that corpus became regression data. The second round used a different, previously undisclosed corpus. Repairs after those rounds were evaluated as regression work, not fresh blindness.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Routing evaluation&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Correct&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Accuracy&lt;/th&gt;
&lt;th align=&quot;right&quot;&gt;Majority baseline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Round 1&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;9/24&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;37.5%&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;25%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Round 2&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;8/24&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;33.3%&lt;/td&gt;
&lt;td align=&quot;right&quot;&gt;25%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The training-majority baseline is 25% in both rounds. These are routing results, not measures of object recognition or language understanding. An emoji input could still be classified as search, and reported confidence had no independent calibration. A prediction therefore cannot grant tool permissions.&lt;/p&gt;
&lt;h2&gt;Passing repairs must not erase failures&lt;/h2&gt;
&lt;p&gt;The second round found invalid training schedules, partial weight mutation before a later bad sample was rejected, control characters in URLs, truthy string authorization, alias shadowing and missing backup lineage checks. AGY handled routing and URL validation. Codex separately patched knowledge review, permissions, typed corrections, transactions and rollback checks.&lt;/p&gt;
&lt;p&gt;The final full regression run passed &lt;strong&gt;85 tests, with zero failures, errors or skips and exit code 0&lt;/strong&gt;. Another 17 actual CLI invocations exercised candidate properties, approval, aliases, correction, arithmetic and default network denial.&lt;/p&gt;
&lt;p&gt;Codex-authored patch checks are author regression evidence, not an independent audit of those patches. Both rounds’ failures, predictions and false positives remain in the repository. Training accuracy reached 100%, but unseen results still show inadequate competence. The actual checkpoint remains a candidate and was not promoted into production.&lt;/p&gt;
&lt;h2&gt;Resource and network claims stay within the evidence&lt;/h2&gt;
&lt;p&gt;The observed peak RSS was about 17.72 MiB in the standalone training process and 25.34 MiB in a separate CLI process. These are single samples from different processes. RSS did not fall after object unloading, so the experiment does not establish that memory returned to the operating system.&lt;/p&gt;
&lt;p&gt;The 6GB VRAM budget is still a target. CUDA allocations, reserved memory, global GPU footprint and future perception-model requirements were not measured.&lt;/p&gt;
&lt;p&gt;Networking is denied by default. Explicit consent is required before sending a query. Search results remain untrusted links: no automatic knowledge promotion, downstream fetching or CAPTCHA bypass. Live provider success was not verified. Independent-source checks, ownership clustering and complete GEO defenses remain unfinished.&lt;/p&gt;
&lt;h2&gt;What is available for analysis&lt;/h2&gt;
&lt;p&gt;The source and audit snapshot are published in &lt;a href=&quot;https://github.com/jsw-teams/train&quot; rel=&quot;noopener noreferrer&quot;&gt;jsw-teams/train&lt;/a&gt;. A successor commit replaces the current tree while retaining the previous remote history. Public evidence redacts machine paths; full raw execution records remain local. Exact dataset, checkpoint and frozen-corpus bytes are preserved so newline conversion cannot invalidate their hashes.&lt;/p&gt;
&lt;p&gt;The next stage needs representative material with traceable sources and licenses, plus a new isolated evaluation corpus. The complete learning loop, a scratch language model, natural conversation, photo recognition and speech are not implemented. Published or feedback-exposed questions must not become the next model’s supposedly unseen test.&lt;/p&gt;
&lt;p&gt;See the &lt;a href=&quot;https://www.js.gripe/ama/&quot; rel=&quot;noopener noreferrer&quot;&gt;AMA model family page&lt;/a&gt;, &lt;a href=&quot;https://github.com/jsw-teams/train/blob/48c1bb4c5bb1ba708d91892ad9ad2745c8d41c19/CODEX_REVIEW.md&quot; rel=&quot;noopener noreferrer&quot;&gt;audit report&lt;/a&gt;, &lt;a href=&quot;https://github.com/jsw-teams/train/blob/48c1bb4c5bb1ba708d91892ad9ad2745c8d41c19/TEST_RESULTS.json&quot; rel=&quot;noopener noreferrer&quot;&gt;executed test results&lt;/a&gt; and &lt;a href=&quot;https://github.com/jsw-teams/train/blob/48c1bb4c5bb1ba708d91892ad9ad2745c8d41c19/data/MANIFEST.json&quot; rel=&quot;noopener noreferrer&quot;&gt;training-data manifest&lt;/a&gt;. Release of the final capability target remains blocked.&lt;/p&gt;
</content:encoded></item><item><title>Develop a theme for EdgePress</title><link>https://www.js.gripe/2026/10/edgepress-theme-development/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/edgepress-theme-development/</guid><pubDate>Sun, 11 Oct 2026 00:00:00 GMT</pubDate><dc:date>2026-10-11T00:00:00.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>Create a local theme, edit HTML partials and styles, and validate real content.</description><category>edgepress</category><category>EdgePress</category><category>practical guide</category><content:encoded>&lt;p&gt;A theme changes how the site is presented while article and page files keep their content. Start with a local theme, get the shared layout right, and check the same content in both wide and narrow windows.&lt;/p&gt;
&lt;h2&gt;Create and select a theme&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;npx edgepress theme list
npx edgepress theme create my-theme
npx edgepress theme use my-theme
npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;theme create&lt;/code&gt; copies the default layout scaffold and creates an empty &lt;code&gt;assets/style.css&lt;/code&gt; for your styles. It does not create a finished visual design. Select an installed theme with &lt;code&gt;theme use&lt;/code&gt;, or install a published theme package with &lt;code&gt;npx edgepress theme install &amp;lt;package&amp;gt;&lt;/code&gt;. If the site has a root &lt;code&gt;theme:&lt;/code&gt; entry in &lt;code&gt;config.yml&lt;/code&gt;, that selects the theme; older projects can use &lt;code&gt;paths.theme&lt;/code&gt; in the build configuration.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;themes/my-theme/
  theme.json
  layouts/layout.html
  layouts/partials/head.html
  layouts/partials/header.html
  layouts/partials/footer.html
  assets/style.css
  assets/data-saver-theme.css
  package.json
  README.md
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Build the shared layout first&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;html lang=&quot;{{site.language}}&quot;&amp;gt;
&amp;lt;head&amp;gt;{{&amp;gt; head}}&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;a class=&quot;skip-link&quot; href=&quot;https://www.js.gripe/2026/10/edgepress-theme-development/#main&quot;&amp;gt;{{site.skipToContent}}&amp;lt;/a&amp;gt;
  {{&amp;gt; header}}
  &amp;lt;main id=&quot;main&quot;&amp;gt;{{{page.content}}}&amp;lt;/main&amp;gt;
  {{&amp;gt; footer}}
  &amp;lt;script defer src=&quot;https://www.js.gripe/edgepress/language-select.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Templates are HTML partials. &lt;code&gt;{{page.title}}&lt;/code&gt; and other ordinary placeholders are escaped; &lt;code&gt;{{{page.content}}}&lt;/code&gt; inserts the framework&apos;s already-rendered content. Keep the head partial, consent support, language navigation and the framework&apos;s local runtime. A theme should not copy those browser implementations.&lt;/p&gt;
&lt;p&gt;Preserve the skip link, one main landmark, visible keyboard focus and one page-level H1. Use a full-height body and a growing main so the footer stays at the bottom on short pages. Test long titles, articles, project lists, empty states and the localized 404 page.&lt;/p&gt;
&lt;h2&gt;Style real page elements&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;assets/style.css&lt;/code&gt; for your palette, typography, spacing and responsive rules. Built-in font variables are &lt;code&gt;--edgepress-font-sans&lt;/code&gt;, &lt;code&gt;--edgepress-font-serif&lt;/code&gt; and &lt;code&gt;--edgepress-font-mono&lt;/code&gt;; font assets remain local. Style the page-builder rows and cells, article body, navigation, tables, code, project recordings and notices. Avoid fixed desktop widths that overflow on phones.&lt;/p&gt;
&lt;p&gt;Keep editorial images in &lt;code&gt;content/assets/&lt;/code&gt; and only theme-owned decoration in the theme assets. Local CSS and JavaScript and their dependencies receive content hashes at build time. The output preserves one-year immutable caching for those hashes while HTML and unversioned metadata revalidate.&lt;/p&gt;
&lt;h2&gt;Add behavior without changing the publishing model&lt;/h2&gt;
&lt;p&gt;Small local theme interactions can live in &lt;code&gt;assets/&lt;/code&gt;. Build-time plugins belong in the paths listed in &lt;code&gt;edgepress.config.mjs&lt;/code&gt;; they are trusted Node code. Optional external services belong in the flat &lt;code&gt;plugins.consent.services&lt;/code&gt; list in &lt;code&gt;config.yml&lt;/code&gt;, with a name, purpose, data disclosure and current service consent before requests. Keep comments in iask rather than building a separate comment system into the theme.&lt;/p&gt;
&lt;p&gt;Images, media galleries and document elements use the existing reusable viewers. Preserve their focus handling, consent gates and lazy loading. Do not copy a player or document engine to adjust its colors.&lt;/p&gt;
&lt;h2&gt;Check the theme with actual content&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;npm run build&lt;/code&gt; and &lt;code&gt;npm run check&lt;/code&gt;, then inspect light and dark palettes, keyboard navigation, translated menus and mobile layouts. Test an article with code and tables, a two-column page, a recording with its seek bar and a document preview. A successful build checks structure; you still need to look at the resulting page.&lt;/p&gt;
&lt;p&gt;A lightweight text variant is optional. If you add one, test its actual request behavior as well as its layout: &lt;a href=&quot;https://www.js.gripe/2026/10/edgepress-data-saving/&quot; rel=&quot;noopener noreferrer&quot;&gt;text-view configuration&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.js.gripe/2026/10/edgepress-content-and-project-guides/&quot; rel=&quot;noopener noreferrer&quot;&gt;Writing and Pages&lt;/a&gt; · &lt;a href=&quot;https://www.js.gripe/theme-development/&quot; rel=&quot;noopener noreferrer&quot;&gt;Theme element reference&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Configure optional text view in EdgePress</title><link>https://www.js.gripe/2026/10/edgepress-data-saving/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/edgepress-data-saving/</guid><pubDate>Sun, 11 Oct 2026 00:00:00 GMT</pubDate><dc:date>2026-10-11T14:32:28.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>Enable a theme-supported text view and test loading, consent and navigation.</description><category>edgepress</category><category>EdgePress</category><category>practical guide</category><content:encoded>&lt;p&gt;Text view is useful when the full page takes too long to load or when a reader wants to save data. It is optional, and a theme must provide a readable lightweight variant before the site enables it. The default view stays full; the reader chooses whether to switch.&lt;/p&gt;
&lt;h2&gt;Enable it in site configuration&lt;/h2&gt;
&lt;p&gt;Merge this into the existing &lt;code&gt;site&lt;/code&gt; object in &lt;code&gt;config.yml&lt;/code&gt; rather than adding a second &lt;code&gt;site&lt;/code&gt; key:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;site:
  dataSaver:
    enabled: true
    debug: false
    mode: auto
    detectSlowConnection: true
    respectBrowserPreference: false
    promptAfterMs: 5000
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables the optional loading and text-view flow. Default: false.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mode: auto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Starts with full view unless the reader has made an explicit choice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;detectSlowConnection&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Offers text view while the first screen waits for resources. It does not switch automatically from a network speed label.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;promptAfterMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets the delayed offer threshold; 5000 means five seconds. Valid range: 100–60000.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;respectBrowserPreference: false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keeps the initial choice independent of browser data-saving hints.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;debug: false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keeps the empty &lt;code&gt;?data-save&lt;/code&gt; preview entry disabled on the public site.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;A saved reader choice takes precedence over the site&apos;s automatic starting mode. &lt;code&gt;mode: text&lt;/code&gt; or &lt;code&gt;mode: full&lt;/code&gt; changes the configured starting preference; neither is needed for the flow above.&lt;/p&gt;
&lt;h2&gt;Let the theme define the lightweight presentation&lt;/h2&gt;
&lt;p&gt;Merge &lt;code&gt;dataSaver&lt;/code&gt; into the active theme&apos;s &lt;code&gt;theme.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;name&quot;: &quot;my-theme&quot;,
  &quot;renderer&quot;: &quot;layouts/layout.html&quot;,
  &quot;templateEngine&quot;: &quot;edgepress-partials-v1&quot;,
  &quot;dataSaver&quot;: {
    &quot;stylesheet&quot;: &quot;data-saver-theme.css&quot;,
    &quot;scripts&quot;: [],
    &quot;layoutStyles&quot;: [&quot;style.css&quot;]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The stylesheet and scripts are paths relative to the theme&apos;s &lt;code&gt;assets/&lt;/code&gt; directory. &lt;code&gt;layoutStyles&lt;/code&gt; lists shared published CSS; the build compiles it with font-face rules and URL declarations removed. This helps retain real navigation, rows, cards and footer geometry without downloading decorative assets. The variant stylesheet supplies its own typography and colors. Keep titles, article excerpts, timestamps, captions and visible focus usable.&lt;/p&gt;
&lt;p&gt;Text view uses system fonts and defers pictures, project clips, document engines and optional service widgets. Readers can request individual media or return to full view. Charts and screenshots still need useful descriptions; saving data should not remove the explanation that makes an article understandable.&lt;/p&gt;
&lt;h2&gt;Keep service choice separate from media loading&lt;/h2&gt;
&lt;p&gt;Accepting consent does not download every optional widget. A service request still needs current per-service consent and a reason to load that widget. For a remote document, the source must also permit CORS. Keep the flat service list and each service&apos;s disclosures; there are no category switches to enable whole groups.&lt;/p&gt;
&lt;p&gt;For dynamic theme media, call &lt;code&gt;window.edgepressDataSaver?.deferMedia(element, activate, description)&lt;/code&gt; before assigning a URL. Make &lt;code&gt;activate&lt;/code&gt; assign the URL only when requested, and provide a fallback when data-saving support is disabled. Mark nonessential full-view scripts with &lt;code&gt;data-data-optional&lt;/code&gt;. Hiding an image after setting &lt;code&gt;src&lt;/code&gt; is too late to save its request.&lt;/p&gt;
&lt;h2&gt;Test the waiting screen and navigation&lt;/h2&gt;
&lt;p&gt;Throttle actual resource transfers to about 100 KB/s, open a fresh page, and switch to text view while full styles and media are still pending. Check that the waiting screen&apos;s controls are keyboard accessible, progress reflects completed resources, late resources do not replace the chosen view, and article navigation preserves the choice. Inspect request logs for fonts, pictures, clips and widgets; hidden pixels are not proof that a request was avoided.&lt;/p&gt;
&lt;p&gt;Also test the return to full view, mobile footer boundaries and a page without JavaScript. &lt;code&gt;debug: true&lt;/code&gt; permits the empty &lt;code&gt;?data-save&lt;/code&gt; test entry; when it is disabled, that entry goes to the localized static 404 page. This client navigation does not prove an HTTP 404 from the host. Keep debug false after testing.&lt;/p&gt;
&lt;p&gt;Each site keeps command entries under its own &lt;code&gt;tools/&lt;/code&gt; and regression cases under &lt;code&gt;tests/&lt;/code&gt;. This site provides &lt;code&gt;npm run test:data-saver&lt;/code&gt; and &lt;code&gt;npm run verify:performance&lt;/code&gt;. The benchmark uses a cold cache, gzip, 100 KB/s and 150 ms latency, reporting first contentful paint separately from readable content. Shared implementations come from the pinned EdgePress dependency. Theme developers can follow the &lt;a href=&quot;https://github.com/jsw-teams/edgepress/blob/main/tools/verification/README.md&quot; rel=&quot;noopener noreferrer&quot;&gt;public tool guide&lt;/a&gt; with their own paths and locales, without access to this site&apos;s source. Anonymous local fixtures do not verify live services or publishing accounts.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.js.gripe/2026/10/edgepress-content-and-project-guides/&quot; rel=&quot;noopener noreferrer&quot;&gt;Writing and Pages&lt;/a&gt; · &lt;a href=&quot;https://www.js.gripe/2026/10/edgepress-theme-development/&quot; rel=&quot;noopener noreferrer&quot;&gt;Theme development&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Read documents inside an EdgePress page</title><link>https://www.js.gripe/2026/10/doc-views-inline-reading/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/doc-views-inline-reading/</guid><pubDate>Sun, 11 Oct 2026 00:00:00 GMT</pubDate><dc:date>2026-10-11T00:00:00.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>Add a local document, retain its reading controls and check the page in full and text view.</description><category>doc-views</category><content:encoded>&lt;p&gt;The document viewer lets a reader open Word, PDF, PowerPoint and Excel content inside an article or page. I keep the source document under &lt;code&gt;content/assets/documents/&lt;/code&gt; and use its site path. The preview opens in place without moving keyboard focus; a title belongs to the document element and appears once.&lt;/p&gt;
&lt;p&gt;An article uses a standalone line in its Markdown body. Replace this example with a file you have added to the site:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;!document[Project report](/documents/project-report.pdf)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a JSON Page, place a document element inside one of the cell&apos;s element arrays:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;type&quot;: &quot;document&quot;,
  &quot;title&quot;: &quot;Project report&quot;,
  &quot;src&quot;: &quot;/documents/project-report.pdf&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The preview shows separated pages in a continuous reading area. Spreadsheets retain worksheet navigation; the surrounding controls follow the site palette while the document keeps its own colors. These formats have different parsing costs, so I check the actual sample instead of assuming every file streams with constant memory.&lt;/p&gt;
&lt;p&gt;Text view defers the viewer until the reader requests it. A remote direct-file URL needs current service consent and permission from the source&apos;s CORS policy. Granting consent does not mean loading every document or widget. Check scrolling, keyboard focus, narrow screens and switching between full and text view with &lt;code&gt;npm run build&lt;/code&gt; and a local preview.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.js.gripe/doc-views/&quot; rel=&quot;noopener noreferrer&quot;&gt;Document viewer and samples&lt;/a&gt; · &lt;a href=&quot;https://www.js.gripe/2026/10/edgepress-content-and-project-guides/&quot; rel=&quot;noopener noreferrer&quot;&gt;JSON Pages&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Website submissions and review guidelines</title><link>https://www.js.gripe/2026/10/website-submissions-review/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/website-submissions-review/</guid><pubDate>Fri, 09 Oct 2026 00:00:00 GMT</pubDate><dc:date>2026-10-09T11:16:26.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>How to suggest a personal website, blog or independent project for the JS.GRIPE Links directory. Submit through the comments on this article.</description><category>site-notice</category><category>Links</category><category>Website submissions</category><content:encoded>&lt;p&gt;The &lt;a href=&quot;https://www.js.gripe/links/&quot; rel=&quot;noopener noreferrer&quot;&gt;Links&lt;/a&gt; page is a small collection of personal websites, thoughtful blogs and independent projects. It is not a ranking, a paid placement program or an automatic link exchange. I would like to discover sites with a point of view, whether they are about software, writing, science, art or something else.&lt;/p&gt;
&lt;h2&gt;Submit a website in the comments&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Leave a comment below this article&lt;/strong&gt; with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Website name&lt;/strong&gt; — the name you want shown in the directory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt; — a public website address beginning with &lt;code&gt;https://&lt;/code&gt; or &lt;code&gt;http://&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Short introduction&lt;/strong&gt; — a sentence or two about what you publish or create.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optional icon information&lt;/strong&gt; — only if you own the image or have permission for it to be used.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To comment, enable &lt;strong&gt;iask&lt;/strong&gt; (the article discussion service) in Privacy settings and sign in with GitHub if prompted. Comments are visible to other readers: please do not post private contact details, passwords or other sensitive information. If comments are unavailable, you can also use the &lt;a href=&quot;https://www.js.gripe/contact/&quot; rel=&quot;noopener noreferrer&quot;&gt;contact page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You may recommend your own site or one you genuinely enjoy. Please do not submit someone else&apos;s logo as though you have the right to license it.&lt;/p&gt;
&lt;h2&gt;How I review submissions&lt;/h2&gt;
&lt;p&gt;I review submissions manually. I look for authentic content, a clear purpose and something useful, distinctive or interesting to explore. There is no traffic threshold, technology requirement or obligation to link back to JS.GRIPE.&lt;/p&gt;
&lt;p&gt;I may decline or remove sites primarily used for spam, misleading impersonation, malicious downloads, intrusive redirects or content unsuitable for this directory. Submitting a link does &lt;strong&gt;not&lt;/strong&gt; guarantee publication, and I cannot promise a review or response time.&lt;/p&gt;
&lt;p&gt;Approved entries are maintained as static content. If an icon is used, it is stored locally after its rights are checked; the directory does not fetch a submitted site&apos;s icon when visitors view the page.&lt;/p&gt;
&lt;h2&gt;Corrections and removals&lt;/h2&gt;
&lt;p&gt;Websites change. If a listing has the wrong name, leads somewhere unexpected or should be removed, please use the &lt;a href=&quot;https://www.js.gripe/contact/&quot; rel=&quot;noopener noreferrer&quot;&gt;contact page&lt;/a&gt;. You can also leave a follow-up comment, but avoid posting private details publicly.&lt;/p&gt;
&lt;p&gt;External links lead to independently operated websites with their own privacy practices. JS.GRIPE shows a destination prompt before an ordinary external click.&lt;/p&gt;
&lt;p&gt;Thanks for sharing a corner of the web worth exploring.&lt;/p&gt;
</content:encoded></item><item><title>Video at your pace, pictures in detail</title><link>https://www.js.gripe/2026/10/media-viewer-play-and-inspect/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/media-viewer-play-and-inspect/</guid><pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate><dc:date>2026-10-30T01:04:41.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>The seek bar occupies its own row. Current time and total duration remain together; dragging moves to a real position. The demonstration pauses to show the resulting timestamp and the selected 2× playback rate.</description><category>media-viewer</category><content:encoded>&lt;p&gt;The seek bar occupies its own row. Current time and total duration remain together; dragging moves to a real position. The demonstration pauses to show the resulting timestamp and the selected 2× playback rate.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.js.gripe/images/previews/media-videojs10.da5382978c5b7462.gif&quot; alt=&quot;Video at your pace, pictures in detail&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Original detail when you open a picture&lt;/h2&gt;
&lt;p&gt;Cards and posts request optimized previews. Clicking a detail image opens its unchanged original; zoom and drag to inspect it. The close control stays outside the picture, and closing returns focus to the image that opened it.&lt;/p&gt;
&lt;h2&gt;A component, rather than page-specific controls&lt;/h2&gt;
&lt;p&gt;Video.js 10 HTML components and hls.js are bundled locally. Gallery navigation mounts only the chosen attachment and disposes previous playback. The integrating website supplies sources, consent and theme colors. The library itself does not publish or store content.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.js.gripe/media-viewer/&quot; rel=&quot;noopener noreferrer&quot;&gt;Project and documentation&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Photos and video in one story: publish, then share</title><link>https://www.js.gripe/2026/10/ishare-publish-and-share/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/ishare-publish-and-share/</guid><pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate><dc:date>2026-10-11T14:11:57.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>Write the story first. Add photos and video beneath it, then publish one post. The centered progress dialog distinguishes transferred bytes, media preparation and publication. On success, view the post, open its share sh</description><category>ishare</category><content:encoded>&lt;p&gt;Write the story first. Add photos and video beneath it, then publish one post. The centered progress dialog distinguishes transferred bytes, media preparation and publication. On success, view the post, open its share sheet or visit your profile.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.js.gripe/images/previews/ishare-workflow-en.7ba2841eb3c831a3.gif&quot; alt=&quot;Photos and video in one story: publish, then share&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Share the complete story, or individual attachments&lt;/h2&gt;
&lt;p&gt;The Link tab gives a public post URL. Embed supplies an iframe containing the complete caption, author and gallery; compatible oEmbed hosts discover it from the link. Markdown gives each image its own link and videos a playback-page link. In EdgePress, register the service first; visible embeds load after visitor consent.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.js.gripe/images/previews/ishare-sharing-en.be3be5b798c509d6.gif&quot; alt=&quot;Paste the shared link into an article and browse its embedded gallery&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Put the post in an EdgePress article&lt;/h2&gt;
&lt;p&gt;Append the ishare entry to the existing services list in config.yml.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;plugins:
  consent:
    services:
      - id: ishare
        provider: oembed
        enabled: true
        name: ishare
        backendUrl: https://ishare.js.gripe
        purpose: Display the shared photos and video after consent.
        dataCategories: Requested media and IP address.
        recipient: ishare
        retention: Until the publisher deletes the post.
        privacyUrl: https://ishare.js.gripe/privacy/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can place the embed directly in the Markdown body with &lt;code&gt;!embed[service](URL)&lt;/code&gt; syntax. Replace the example URL with the link copied from your post:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;!embed[ishare](https://ishare.js.gripe/s/0123456789abcdef0123456789abcdef)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The page reserves a themed placeholder first, then loads the caption and attachment gallery after consent as it approaches the viewport. Save, run &lt;code&gt;edgepress server&lt;/code&gt;, and open the article from the homepage to verify it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;!embed[ishare](https://ishare.js.gripe/s/0726bea158a144129dd3b70468f1a0fc)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;History belongs on your profile&lt;/h2&gt;
&lt;p&gt;Open My profile from the avatar menu. Visitors see your published posts; only you see the deletion controls and homepage inclusion switches. The personal center separately handles your display name, biography and data rights.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.js.gripe/ishare/&quot; rel=&quot;noopener noreferrer&quot;&gt;Project and documentation&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Use EdgePress for writing and Pages</title><link>https://www.js.gripe/2026/10/edgepress-content-and-project-guides/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/edgepress-content-and-project-guides/</guid><pubDate>Tue, 06 Oct 2026 00:00:00 GMT</pubDate><dc:date>2026-10-11T15:08:30.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>Start the site, write an article, read page containment and build static output.</description><category>edgepress</category><category>EdgePress</category><category>practical guide</category><content:encoded>&lt;p&gt;I use EdgePress to keep writing and layout in ordinary files. Articles are Markdown. Homepages and project pages are JSON layouts. The build produces static HTML and local assets in &lt;code&gt;dist/&lt;/code&gt;; there is no site database to maintain for publishing an article.&lt;/p&gt;
&lt;h2&gt;Get a site running&lt;/h2&gt;
&lt;p&gt;For the current repository checkout, use Node.js 22.12 or newer and run these commands from the project root:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;npm ci
npm run new -- post &quot;A quiet afternoon&quot;
npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The new-post command prints the path of the file it created. Open that file, keep the metadata between the two &lt;code&gt;---&lt;/code&gt; lines, and write below the second line. The preview rebuilds when source files change. Create an article with the CLI once; subsequent edits happen in the editor.&lt;/p&gt;
&lt;p&gt;For a new site, install the published GitHub source commit used by this site and initialize an empty directory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;mkdir my-site
cd my-site
npm install edgepress@https://codeload.github.com/jsw-teams/edgepress/tar.gz/ac42e0046d022a318e4eef21e56e2e459584b2a5 --save-exact
npx edgepress init
npm install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The npm release and the latest GitHub checkout can differ. Keep the site&apos;s dependency version and lockfile together. A site that already depends on EdgePress should use its existing package scripts rather than install a global copy.&lt;/p&gt;
&lt;h2&gt;Know which file to edit&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;config.yml                    (site, theme, privacy, consent services)
edgepress.config.mjs           (paths, locales, permalink, build plugins)
content/posts/&amp;lt;post-id&amp;gt;/en.md   (article metadata + Markdown)
content/pages/&amp;lt;page-id&amp;gt;/en.json   (page metadata + rows and elements)
content/assets/images/walk.jpg (published as /images/walk.jpg)
themes/&amp;lt;name&amp;gt;/                 (shared layouts and visual styles)
dist/                         (generated output)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Set your title, canonical URL, navigation, footer and privacy details in &lt;code&gt;config.yml&lt;/code&gt;. Enable languages and change build paths in &lt;code&gt;edgepress.config.mjs&lt;/code&gt;. A locale filename is lowercase: Pages use &lt;code&gt;en.json&lt;/code&gt; or &lt;code&gt;zh-cn.json&lt;/code&gt;, while articles use &lt;code&gt;en.md&lt;/code&gt; or &lt;code&gt;zh-cn.md&lt;/code&gt;. Its &lt;code&gt;lang&lt;/code&gt; must match an enabled locale. Each article or page gets a directory containing its translations.&lt;/p&gt;
&lt;h2&gt;Write the article&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;---
title: A quiet afternoon
date: 2026-10-11T10:00:00+08:00
lang: en
category: blog
tags: [walk, photography]
---

We stopped by the water.

## A small detail

![Reflections by the shore](/images/walk.jpg)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The CLI supplies the current timestamp and site language; the example above shows the shape of the file. The theme renders the article title, so use &lt;code&gt;##&lt;/code&gt; for sections rather than repeating the title as an H1. Copy the photograph to &lt;code&gt;content/assets/images/walk.jpg&lt;/code&gt; before building.&lt;/p&gt;
&lt;p&gt;One &lt;code&gt;category&lt;/code&gt; chooses the article&apos;s section. &lt;code&gt;blog&lt;/code&gt; and &lt;code&gt;uncategorized&lt;/code&gt; belong to this site&apos;s everyday observations; &lt;code&gt;edgepress&lt;/code&gt;, &lt;code&gt;ishare&lt;/code&gt;, &lt;code&gt;iask&lt;/code&gt; and &lt;code&gt;media-viewer&lt;/code&gt; belong to product notes. Tags describe the article and do not select its homepage list.&lt;/p&gt;
&lt;h2&gt;Read a page from the outside in&lt;/h2&gt;
&lt;p&gt;Page (rows) → row (columns, cells) → cell (ordered elements) → element (type, content). Here is a complete two-column page:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;title&quot;: &quot;Project notes&quot;,
  &quot;lang&quot;: &quot;en&quot;,
  &quot;slug&quot;: &quot;notes&quot;,
  &quot;blocks&quot;: [
    {
      &quot;columns&quot;: 2,
      &quot;cells&quot;: [
        [
          {
            &quot;type&quot;: &quot;heading&quot;,
            &quot;level&quot;: 2,
            &quot;text&quot;: &quot;What I am making&quot;
          },
          {
            &quot;type&quot;: &quot;text&quot;,
            &quot;text&quot;: &quot;A small tool for everyday writing.&quot;
          }
        ],
        [
          {
            &quot;type&quot;: &quot;post-list&quot;,
            &quot;title&quot;: &quot;Notes&quot;,
            &quot;category&quot;: &quot;edgepress&quot;,
            &quot;count&quot;: 3
          }
        ]
      ]
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save it as &lt;code&gt;content/pages/notes/en.json&lt;/code&gt;. Metadata and blocks share one JSON object; there are no YAML delimiters or Markdown body. VS Code can identify and fold blocks, each row, cells, each cell and its elements. The local &lt;code&gt;$schema&lt;/code&gt; reference supplies field help and checks the structure. &lt;code&gt;columns: 2&lt;/code&gt; needs two cell arrays; their elements render in order. A section’s blocks contain child elements. Use paragraphs for separate prose paragraphs and \n inside code strings for line breaks. Legacy Markdown/YAML Pages still build, but keep one format per page language.&lt;/p&gt;
&lt;h2&gt;Preview, then publish the generated files&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;npm run build&lt;/code&gt; to validate and generate the website; &lt;code&gt;npm run check&lt;/code&gt; writes the page report under &lt;code&gt;tools/reports/&lt;/code&gt;. Inspect the homepage, article, language links and mobile layout locally. Publish &lt;code&gt;dist/&lt;/code&gt; through your existing host and deployment configuration. Generated HTML is output, so make corrections in the source files.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.js.gripe/2026/10/edgepress-theme-development/&quot; rel=&quot;noopener noreferrer&quot;&gt;Theme development&lt;/a&gt; · &lt;a href=&quot;https://www.js.gripe/2026/10/edgepress-data-saving/&quot; rel=&quot;noopener noreferrer&quot;&gt;Optional text view&lt;/a&gt; · &lt;a href=&quot;https://www.js.gripe/edgepress/quick-start/&quot; rel=&quot;noopener noreferrer&quot;&gt;Project and quick start&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>iask: optional website discussions</title><link>https://www.js.gripe/2026/10/reporelay-introduction/</link><guid isPermaLink="true">https://www.js.gripe/2026/10/reporelay-introduction/</guid><pubDate>Sat, 03 Oct 2026 00:00:00 GMT</pubDate><dc:date>2026-10-07T02:08:42.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>iask owns the discussion interface and backend. Register it as an optional service in the website config.yml; readers decide whether to load it. GitHub sign-in is needed to publish, while public discussions remain readab</description><category>iask</category><category>github-app</category><category>open-source</category><content:encoded>&lt;p&gt;iask owns the discussion interface and backend. Register it as an optional service in the website config.yml; readers decide whether to load it. GitHub sign-in is needed to publish, while public discussions remain readable without signing in. Pictures and bear or panda stickers accompany text, with stickers visible inside the editor.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.js.gripe/images/previews/iask-workflow-en.1bf8aec37dc8f9cb.gif&quot; alt=&quot;iask: optional website discussions&quot; loading=&quot;lazy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The interface follows the website palette, available width and language. Cloudflare and Netlify have platform adapters; Vercel additionally requires persistent PostgreSQL. Check the deployment guide for current configuration and callbacks.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jsw-teams/iask&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub&lt;/a&gt; / &lt;a href=&quot;https://github.com/jsw-teams/iask/blob/main/docs/edgepress.md&quot; rel=&quot;noopener noreferrer&quot;&gt;Documentation&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Markdown and embeds for an EdgePress article</title><link>https://www.js.gripe/2026/09/markdown-syntax-guide/</link><guid isPermaLink="true">https://www.js.gripe/2026/09/markdown-syntax-guide/</guid><pubDate>Wed, 30 Sep 2026 00:00:00 GMT</pubDate><dc:date>2026-10-11T14:11:57.000Z</dc:date><dc:creator>toewpq</dc:creator><dc:language>en</dc:language><description>Practical article syntax, local media and consent-controlled document and share embeds.</description><category>edgepress</category><category>EdgePress</category><category>practical guide</category><content:encoded>&lt;p&gt;An EdgePress article has YAML metadata followed by Markdown. Use the CLI to create the file, then edit the body below the second &lt;code&gt;---&lt;/code&gt;. This example collects the syntax I use for a practical note:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;## What I noticed

A paragraph with **emphasis**, a [link](https://example.org/) and `inline code`.

- A short list
- Another point

&amp;gt; A quoted observation.

| Step | Result |
| --- | --- |
| Build | Static HTML |

- [x] Draft
- [ ] Preview

![A photograph](/images/walk.jpg)

!document[Report](/documents/report.pdf)

!embed[ishare](https://ishare.js.gripe/s/EXAMPLE)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The site renders headings, paragraphs, emphasis, links, lists, quotations, fenced code, GFM tables and task lists. Put code inside triple backticks with a language name. The page already displays the article title; body sections start at &lt;code&gt;##&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Copy media into &lt;code&gt;content/assets/&lt;/code&gt; before referencing its site-root path. A picture opens the shared image viewer; an explicit original URL is requested on activation. A local document is previewed automatically by the reusable document viewer. External documents need a registered service, current consent and CORS.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;!embed[ishare](URL)&lt;/code&gt; is a standalone line, using the real published share URL instead of EXAMPLE. Register the service in &lt;code&gt;plugins.consent.services&lt;/code&gt; before using it; requests wait for consent and demand. The same extension can be used in a Pages text element. Do not put embeds into article front matter.&lt;/p&gt;
&lt;p&gt;Pages use structured elements, so a whole Markdown article should not be pasted into a Pages text field. Use the &lt;a href=&quot;https://www.js.gripe/2026/10/edgepress-content-and-project-guides/&quot; rel=&quot;noopener noreferrer&quot;&gt;complete Pages example&lt;/a&gt; for layout. Preview with &lt;code&gt;npm run dev&lt;/code&gt; and build before publication.&lt;/p&gt;
</content:encoded></item></channel></rss>