diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index 0215369..a997bfc 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -39,6 +39,9 @@ jobs:
# Zola fails the build on broken internal links.
run: zola build
+ - name: Verify editor, demo outputs and publication downloads
+ run: python3 -m unittest discover -s scripts -p 'test_*.py' -v
+
- name: Verify the build produced pages
run: |
pages=$(find public -name '*.html' | wc -l)
@@ -64,4 +67,4 @@ jobs:
uses: actions/checkout@v6
- name: Verify advertised downloads exist on the public channel
- run: python3 scripts/check-release-links.py
\ No newline at end of file
+ run: python3 scripts/check-release-links.py
diff --git a/config.toml b/config.toml
index 8878c85..d8014fe 100644
--- a/config.toml
+++ b/config.toml
@@ -51,7 +51,7 @@ include_content = true
[extra]
version = "1.21.16"
-release_version = "1.21.18"
+release_version = "1.21.19"
navbar_items = [
{ code = "en", nav_items = [
diff --git a/content/how-tos/synonym-search.md b/content/how-tos/synonym-search.md
index 32bf89a..8ca51cc 100644
--- a/content/how-tos/synonym-search.md
+++ b/content/how-tos/synonym-search.md
@@ -5,6 +5,8 @@ date = 2026-10-11
[taxonomies]
categories = ["How-tos"]
tags = ["editor", "knowledge-graph", "search"]
+[extra]
+social_image = "/demos/synonym-search/poster.jpg"
+++
Your project brief says **risk**. Later, you search for **concern**. Terraphim connects the two so a change of wording does not hide the decisions you need.
@@ -32,12 +34,12 @@ Download the graph and the clean text. This explicitly shares the document's aut
Use the graph downloaded from that editing session:
```sh
-terraphim-grep concern --thesaurus knowledge-graph.json --paths corpus -C 2 --json
+terraphim-grep concern --thesaurus knowledge-graph.json --paths corpus -C 2 --search-only --json
```
The actual result contains three risk passages and their related decisions, with file and line locations. No generated answer is requested.
-Add `--answer` to the same command for synthesis:
+Replace `--search-only` with `--answer` for synthesis:
```sh
terraphim-grep concern --thesaurus knowledge-graph.json --paths corpus -C 2 --json --answer
@@ -54,9 +56,17 @@ Editing and local search use your local data. Optional synthesis sends the retri
- [Saved Markdown with alternatives](/demos/synonym-search/annotated.md)
- [Actual result without synthesis](/demos/synonym-search/without-synthesis.json)
- [Actual result with synthesis](/demos/synonym-search/with-synthesis.json)
+- [Download the complete example ZIP](/demos/synonym-search/synonym-search-example.zip), including a step-by-step README
+- [English subtitles (SRT)](/demos/synonym-search/captions.srt)
The community garden brief is authored public sample input. The alternatives, downloaded graph, retrieved passages and generated answer are actual outputs. The command-line section displays those JSON fields in a readable presentation; it is not a separate Terraphim interface. The voice is synthetic British English (Emma). Setup is omitted, and the recorded interaction runs at its original speed. [Recording details and file identities](/demos/synonym-search/manifest.json).
-This is a preview of the new export and search flow. It uses the [editor graph-sharing candidate](https://git.terraphim.cloud/terraphim/terraphim-editor/pulls/131) and the [grep synonym/synthesis candidate](https://git.terraphim.cloud/terraphim/terraphim-clients/pulls/385); older installed binaries may differ. A fresh synthesis run may produce different wording.
+## Reproduce it with released builds
+
+The recording preserves its original candidate-build identities. The same flow was separately verified on 12 October 2026 using the published [Editor v0.1.0-rc.1 prerelease](https://github.com/terraphim/terraphim-editor/releases/tag/v0.1.0-rc.1) and [terraphim-grep v1.21.19](https://github.com/terraphim/terraphim-clients/releases/tag/v1.21.19). The editor is still an RC; the video is not a claim that every host or feature is fully qualified.
+
+Download the example ZIP, extract it, and follow `README.md`. It uses the same graph and clean brief as the recording. Verify `terraphim-grep --version` before running the two commands above. The release includes macOS and Linux builds; Windows is not claimed by this example.
+
+Inspect the separate released-binary outputs: [before sharing the synonym graph](/demos/synonym-search/released-before-sharing.json), [without synthesis](/demos/synonym-search/released-without-synthesis.json), and [with synthesis](/demos/synonym-search/released-with-synthesis.json). The baseline used the editor's default role, which lacks the authored synonym: zero passages became three when the exported graph was supplied. The paired modes retrieved the same passages. [Released-build identities, commands and file checksums](/demos/synonym-search/reproduction.json) distinguish this verification from the original recording. A fresh synthesis run may produce different wording.
[Earlier browser and Zed host walkthroughs](/how-tos/knowledge-work-editor/) retain their own recorded versions and limitations. This focused story does not qualify all 41 source-video requirements. The Zed fork remains separate.
diff --git a/scripts/test_synonym_publication.py b/scripts/test_synonym_publication.py
new file mode 100644
index 0000000..596f4ee
--- /dev/null
+++ b/scripts/test_synonym_publication.py
@@ -0,0 +1,80 @@
+"""Exercise the built page and the downloadable reproduction kit."""
+import hashlib
+from html.parser import HTMLParser
+import json
+from pathlib import Path
+import subprocess
+import tempfile
+import unittest
+import zipfile
+
+ROOT = Path(__file__).resolve().parents[1]
+MEDIA = ROOT / "static/demos/synonym-search"
+
+
+class HeadParser(HTMLParser):
+ def __init__(self):
+ super().__init__()
+ self.meta = {}
+ self.canonicals = []
+
+ def handle_starttag(self, tag, attrs):
+ attrs = dict(attrs)
+ if tag == "meta":
+ self.meta[attrs.get("property", attrs.get("name"))] = attrs.get("content")
+ if tag == "link" and attrs.get("rel") == "canonical":
+ self.canonicals.append(attrs["href"])
+
+
+class PublicationTests(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ cls.output = tempfile.TemporaryDirectory()
+ built = Path(cls.output.name) / "site"
+ subprocess.run(["zola", "build", "--output-dir", str(built)], cwd=ROOT, check=True, text=True)
+ cls.html = (built / "how-tos/synonym-search/index.html").read_text()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls.output.cleanup()
+
+ def test_production_metadata_and_privacy_wording(self):
+ head = HeadParser()
+ head.feed(self.html)
+ url = "https://terraphim.ai/how-tos/synonym-search/"
+ self.assertEqual(head.canonicals, [url])
+ self.assertEqual(head.meta["og:url"], url)
+ self.assertEqual(head.meta["og:title"], "Different words. The same idea.")
+ self.assertEqual(head.meta["og:image"], "https://terraphim.ai/demos/synonym-search/poster.jpg")
+ self.assertEqual(head.meta["twitter:card"], "summary_large_image")
+ self.assertNotIn("All data processed locally", self.html)
+ self.assertIn("Optional synthesis uses your configured model provider", self.html)
+ self.assertNotIn("editor graph-sharing candidate", self.html)
+ self.assertIn("v0.1.0-rc.1", self.html)
+ self.assertIn("v1.21.19", self.html)
+
+ def test_released_evidence_and_bundle_match_actual_files(self):
+ receipt = json.loads((MEDIA / "reproduction.json").read_text())
+ self.assertEqual(receipt["grepVersion"], "1.21.19")
+ self.assertEqual(receipt["editorVersion"], "0.1.0-rc.1")
+ before = json.loads((MEDIA / "released-before-sharing.json").read_text())
+ off = json.loads((MEDIA / "released-without-synthesis.json").read_text())
+ on = json.loads((MEDIA / "released-with-synthesis.json").read_text())
+ self.assertEqual(before["chunks"], [])
+ self.assertEqual(off["chunks"], on["chunks"])
+ self.assertEqual(len(off["chunks"]), 3)
+ self.assertIsNone(off["answer"])
+ self.assertTrue(on["answer"]["answer"])
+ self.assertEqual(len(on["answer"]["citations"]), 3)
+ with zipfile.ZipFile(MEDIA / "synonym-search-example.zip") as bundle:
+ self.assertEqual(set(bundle.namelist()), set(receipt["bundleFiles"]))
+ for name in receipt["bundleFiles"]:
+ self.assertEqual(bundle.read(name), (MEDIA / name).read_bytes())
+ for item in receipt["files"]:
+ self.assertEqual(hashlib.sha256((MEDIA / item["file"]).read_bytes()).hexdigest(), item["sha256"])
+ for name in ["reproduction.json", "synonym-search-example.zip", "captions.srt"]:
+ self.assertIn("/demos/synonym-search/" + name, self.html)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/static/demos/synonym-search/README.md b/static/demos/synonym-search/README.md
new file mode 100644
index 0000000..3ae366e
--- /dev/null
+++ b/static/demos/synonym-search/README.md
@@ -0,0 +1,70 @@
+# Different words. The same idea.
+
+Source: https://terraphim.ai/how-tos/synonym-search/
+
+This kit contains the actual graph and clean brief exported in the 80-second
+recording, and separate evidence from released builds. The brief is public sample
+input. The graph and search results are actual outputs.
+
+## Reproduce local retrieval
+
+1. Download terraphim-grep v1.21.19 for your macOS or Linux architecture:
+ https://github.com/terraphim/terraphim-clients/releases/tag/v1.21.19
+ Check the archive against that release's SHA256SUMS, then extract it and put
+ terraphim-grep on PATH. No Windows build is claimed by this kit.
+2. In this extracted directory, run:
+
+```sh
+terraphim-grep --version
+mkdir -p corpus
+cp project-brief.md corpus/project-brief.md
+terraphim-grep concern --thesaurus baseline-role.json --paths corpus -C 2 --search-only --json
+terraphim-grep concern --thesaurus knowledge-graph.json --paths corpus -C 2 --search-only --json
+```
+
+The first search uses the editor's default role, which lacks this authored
+synonym. It returns zero passages. The exported graph connects Risk and Concern;
+the second search returns three Risk passages even though Concern is absent
+from the clean brief. `released-before-sharing.json` and
+`released-without-synthesis.json` contain those actual outputs.
+
+## Compare optional synthesis
+
+Configure a model provider using the client release documentation. Then run:
+
+```sh
+terraphim-grep concern --thesaurus knowledge-graph.json --paths corpus -C 2 --json --answer
+```
+
+Optional synthesis sends the retrieved passages to your configured model
+provider and may incur provider charges. The released verification used
+DeepSeek (deepseek-v4-flash-0731). No credentials are included. Generated wording
+can vary. Compare the chunks and citations, not an exact answer string:
+`released-with-synthesis.json` has the same three chunks as the search-only run.
+Decisions in the source do not prove that the actions have been completed.
+
+## Recreate the editor export
+
+Use https://terraphim.ai/editor/ or the published v0.1.0-rc.1 editor prerelease:
+https://github.com/terraphim/terraphim-editor/releases/tag/v0.1.0-rc.1
+
+Open the brief, add Concern as an alternative to Risk, swap between them, and
+retain Risk in the final text. Choose Knowledge graph from the Markdown export
+menu. The definition is `synonyms:: Risk, Concern`. Export the graph and clean
+text, then use your exported filenames in the commands above. This explicitly
+shares document synonyms; it does not silently update the active role.
+`annotated.md` preserves the alternatives for reopening.
+
+## Provenance
+
+- `manifest.json`, `without-synthesis.json`, and `with-synthesis.json` describe
+ the original candidate-build recording. They have not been relabelled as
+ released-binary evidence.
+- `reproduction.json` and the `released-*.json` files describe the separate
+ v0.1.0-rc.1 / v1.21.19 verification on 12 October 2026.
+- `captions.vtt`, `captions.srt`, and `transcript.txt` accompany the original video.
+ The British female narration is synthetic (Emma, Kokoro).
+- Video and poster are available on the source page; they are not in this ZIP.
+
+This focused flow does not qualify all 41 requirements or every extension.
+The Zed fork remains separate.
diff --git a/static/demos/synonym-search/baseline-role.json b/static/demos/synonym-search/baseline-role.json
new file mode 100644
index 0000000..a8c1b2c
--- /dev/null
+++ b/static/demos/synonym-search/baseline-role.json
@@ -0,0 +1,91 @@
+{
+ "name": "terraphim-editor-default",
+ "data": {
+ "alternative": {
+ "id": 100,
+ "nterm": "alternatives"
+ },
+ "alternatives": {
+ "id": 100,
+ "nterm": "alternatives"
+ },
+ "caf\u00e9": {
+ "id": 4,
+ "nterm": "caf\u00e9"
+ },
+ "choice": {
+ "id": 1,
+ "nterm": "decision"
+ },
+ "coffee shop": {
+ "id": 4,
+ "nterm": "caf\u00e9"
+ },
+ "coffeehouse": {
+ "id": 4,
+ "nterm": "caf\u00e9"
+ },
+ "decision": {
+ "id": 1,
+ "nterm": "decision"
+ },
+ "employ": {
+ "id": 8,
+ "nterm": "use"
+ },
+ "eraser": {
+ "id": 2,
+ "nterm": "eraser"
+ },
+ "honour": {
+ "id": 5,
+ "nterm": "honour"
+ },
+ "judgment": {
+ "id": 1,
+ "nterm": "decision"
+ },
+ "large language model": {
+ "id": 3,
+ "nterm": "llm",
+ "display_value": "LLM"
+ },
+ "llm": {
+ "id": 3,
+ "nterm": "llm",
+ "display_value": "LLM"
+ },
+ "option": {
+ "id": 1,
+ "nterm": "decision"
+ },
+ "options set": {
+ "id": 100,
+ "nterm": "alternatives"
+ },
+ "paperclip": {
+ "id": 7,
+ "nterm": "paperclip"
+ },
+ "privilege": {
+ "id": 5,
+ "nterm": "honour"
+ },
+ "rubber": {
+ "id": 2,
+ "nterm": "eraser"
+ },
+ "use": {
+ "id": 8,
+ "nterm": "use"
+ },
+ "utilise": {
+ "id": 8,
+ "nterm": "use"
+ },
+ "variants": {
+ "id": 100,
+ "nterm": "alternatives"
+ }
+ }
+}
diff --git a/static/demos/synonym-search/captions.srt b/static/demos/synonym-search/captions.srt
new file mode 100644
index 0000000..42e48a8
--- /dev/null
+++ b/static/demos/synonym-search/captions.srt
@@ -0,0 +1,75 @@
+1
+00:00:00,250 --> 00:00:04,443
+When people use different words for the same idea, useful decisions can
+
+2
+00:00:04,443 --> 00:00:06,190
+become surprisingly hard to find.
+
+3
+00:00:07,250 --> 00:00:10,852
+Here is our community garden brief. It records three risks, and the
+
+4
+00:00:10,852 --> 00:00:13,553
+decisions we have made to deal with each one.
+
+5
+00:00:15,250 --> 00:00:18,996
+I add concern as an alternative to risk. Now I can swap
+
+6
+00:00:18,996 --> 00:00:22,742
+between the two words, choosing the wording that fits without losing the
+
+7
+00:00:22,742 --> 00:00:23,366
+other option.
+
+8
+00:00:26,250 --> 00:00:30,840
+The editor connects those alternatives to one idea in the knowledge graph.
+
+9
+00:00:30,840 --> 00:00:35,047
+Here is the actual synonym definition: risk and concern belong together.
+
+10
+00:00:37,250 --> 00:00:41,059
+I download that graph and the finished text. Notice that I leave
+
+11
+00:00:41,059 --> 00:00:43,915
+risk in the document. The word concern is absent.
+
+12
+00:00:46,250 --> 00:00:50,497
+Now I search for concern using the graph I just downloaded. It
+
+13
+00:00:50,497 --> 00:00:54,744
+finds all three risk passages, including the related decisions, even though the
+
+14
+00:00:54,744 --> 00:00:55,805
+wording is different.
+
+15
+00:00:58,250 --> 00:01:02,632
+Without synthesis, I get the original passages and their locations. Adding the
+
+16
+00:01:02,632 --> 00:01:07,014
+answer flag gives me a summary from the same evidence, with source
+
+17
+00:01:07,014 --> 00:01:07,379
+references.
+
+18
+00:01:10,250 --> 00:01:14,735
+The summary keeps decisions distinct from completed actions. From choosing a word
+
+19
+00:01:14,735 --> 00:01:19,219
+to finding a decision, the same knowledge graph keeps the meaning connected.
diff --git a/static/demos/synonym-search/released-before-sharing.json b/static/demos/synonym-search/released-before-sharing.json
new file mode 100644
index 0000000..e88fb9a
--- /dev/null
+++ b/static/demos/synonym-search/released-before-sharing.json
@@ -0,0 +1,13 @@
+{
+ "chunks": [],
+ "answer": null,
+ "concepts": [],
+ "sufficiency": "RlmInsufficient",
+ "sufficiency_explanation": "No chunks or knowledge-graph concepts matched the query; there is nothing to synthesise an answer from.",
+ "stats": {
+ "search_latency_ms": 4,
+ "rlm_latency_ms": null,
+ "chunks_returned": 0,
+ "kg_hits": 0
+ }
+}
diff --git a/static/demos/synonym-search/released-with-synthesis.json b/static/demos/synonym-search/released-with-synthesis.json
new file mode 100644
index 0000000..7776363
--- /dev/null
+++ b/static/demos/synonym-search/released-with-synthesis.json
@@ -0,0 +1,71 @@
+{
+ "chunks": [
+ {
+ "content": "Risk: Timber may arrive late. Decision: Keep a backup supplier.",
+ "source": "project-brief.md",
+ "line_start": 7,
+ "line_end": 7,
+ "relevance_score": 2.0,
+ "haystack": "code"
+ },
+ {
+ "content": "Risk: Rain may delay planting. Decision: Reserve a covered area.",
+ "source": "project-brief.md",
+ "line_start": 10,
+ "line_end": 10,
+ "relevance_score": 2.0,
+ "haystack": "code"
+ },
+ {
+ "content": "Risk: Volunteers may cancel. Decision: Keep a reserve list.",
+ "source": "project-brief.md",
+ "line_start": 13,
+ "line_end": 13,
+ "relevance_score": 2.0,
+ "haystack": "code"
+ }
+ ],
+ "answer": {
+ "answer": "The project brief records three concerns/risks, each paired with a decision. It does not establish whether any of these decisions have been implemented or completed. The concerns are: timber may arrive late, rain may delay planting, and volunteers may cancel. The corresponding decisions are: keep a backup supplier, reserve a covered area, and keep a reserve list.",
+ "citations": [
+ {
+ "source": "project-brief.md",
+ "line": 7,
+ "excerpt": "Risk: Timber may arrive late. Decision: Keep a backup supplier."
+ },
+ {
+ "source": "project-brief.md",
+ "line": 10,
+ "excerpt": "Risk: Rain may delay planting. Decision: Reserve a covered area."
+ },
+ {
+ "source": "project-brief.md",
+ "line": 13,
+ "excerpt": "Risk: Volunteers may cancel. Decision: Keep a reserve list."
+ }
+ ],
+ "confidence": 1.0
+ },
+ "concepts": [
+ {
+ "id": 0,
+ "name": "concern",
+ "display_value": null,
+ "score": 1.0
+ },
+ {
+ "id": 0,
+ "name": "risk",
+ "display_value": null,
+ "score": 1.0
+ }
+ ],
+ "sufficiency": "RlmSynthesis",
+ "sufficiency_explanation": "Found 3 chunks (coverage 1.00, KG confidence 1.00); the answer was synthesised by the LLM in 15349ms.",
+ "stats": {
+ "search_latency_ms": 15354,
+ "rlm_latency_ms": 15349,
+ "chunks_returned": 3,
+ "kg_hits": 2
+ }
+}
diff --git a/static/demos/synonym-search/released-without-synthesis.json b/static/demos/synonym-search/released-without-synthesis.json
new file mode 100644
index 0000000..3d1b2b5
--- /dev/null
+++ b/static/demos/synonym-search/released-without-synthesis.json
@@ -0,0 +1,51 @@
+{
+ "chunks": [
+ {
+ "content": "Risk: Timber may arrive late. Decision: Keep a backup supplier.",
+ "source": "project-brief.md",
+ "line_start": 7,
+ "line_end": 7,
+ "relevance_score": 2.0,
+ "haystack": "code"
+ },
+ {
+ "content": "Risk: Rain may delay planting. Decision: Reserve a covered area.",
+ "source": "project-brief.md",
+ "line_start": 10,
+ "line_end": 10,
+ "relevance_score": 2.0,
+ "haystack": "code"
+ },
+ {
+ "content": "Risk: Volunteers may cancel. Decision: Keep a reserve list.",
+ "source": "project-brief.md",
+ "line_start": 13,
+ "line_end": 13,
+ "relevance_score": 2.0,
+ "haystack": "code"
+ }
+ ],
+ "answer": null,
+ "concepts": [
+ {
+ "id": 0,
+ "name": "concern",
+ "display_value": null,
+ "score": 1.0
+ },
+ {
+ "id": 0,
+ "name": "risk",
+ "display_value": null,
+ "score": 1.0
+ }
+ ],
+ "sufficiency": "SearchOnly",
+ "sufficiency_explanation": "Found 3 chunks but diversity 1 < 2; returning search results only (pass --answer or --force-rlm to synthesise).",
+ "stats": {
+ "search_latency_ms": 2,
+ "rlm_latency_ms": null,
+ "chunks_returned": 3,
+ "kg_hits": 2
+ }
+}
diff --git a/static/demos/synonym-search/reproduction.json b/static/demos/synonym-search/reproduction.json
new file mode 100644
index 0000000..e174a56
--- /dev/null
+++ b/static/demos/synonym-search/reproduction.json
@@ -0,0 +1,152 @@
+{
+ "schema": 1,
+ "verifiedAt": "2026-10-12",
+ "source": "https://terraphim.ai/how-tos/synonym-search/",
+ "editorVersion": "0.1.0-rc.1",
+ "grepVersion": "1.21.19",
+ "editorSourceHead": "05d0aa50151c131db4a3b30b56aba67c00add5fc",
+ "clientsSourceHead": "2d1820e81d2a9458d4d5eca012e7d0e0d2accf49",
+ "editorArchiveSha256": "a514f45276397b93f38d8e14c9e3bde6203bd9c625bc13c81829de31ffa9eb79",
+ "grepMacArmArchiveSha256": "2e9c8fc8b54124458a54120134ae5437f8b7e023df28fc1d515dd059c8f86a3e",
+ "checks": {
+ "graphSha256": "f8648b16470e764bdff5c95064078419d71e8d41d2ca30d4b1508017c166a45d",
+ "textSha256": "ea78a26e4d5a1105891bed67288a924cbbd5656defc95de4c511f4302b8d0d8f",
+ "binarySha256": "ce20e2bf87c6753c717dade4ac7baac98b05d0be9cb39af1f3afbcbf6e5c47de",
+ "binaryVersion": "terraphim-grep 1.21.19",
+ "status": "PASS",
+ "literalConcernAbsent": true,
+ "beforeSharingNoResults": true,
+ "aliasRetrieval": true,
+ "sameRetrievedChunks": true,
+ "sourceExcerptsExact": true,
+ "actualSynthesis": true,
+ "provider": "deepseek/deepseek-v4-flash-0731"
+ },
+ "commands": [
+ [
+ "terraphim-grep",
+ "concern",
+ "--thesaurus",
+ "baseline-role.json",
+ "--paths",
+ "corpus",
+ "-C",
+ "2",
+ "--json",
+ "--search-only"
+ ],
+ [
+ "terraphim-grep",
+ "concern",
+ "--thesaurus",
+ "knowledge-graph.json",
+ "--paths",
+ "corpus",
+ "-C",
+ "2",
+ "--json",
+ "--search-only"
+ ],
+ [
+ "terraphim-grep",
+ "concern",
+ "--thesaurus",
+ "knowledge-graph.json",
+ "--paths",
+ "corpus",
+ "-C",
+ "2",
+ "--json",
+ "--answer"
+ ]
+ ],
+ "bundleFiles": [
+ "README.md",
+ "annotated.md",
+ "baseline-role.json",
+ "knowledge-graph.json",
+ "project-brief.md",
+ "without-synthesis.json",
+ "with-synthesis.json",
+ "released-before-sharing.json",
+ "released-without-synthesis.json",
+ "released-with-synthesis.json",
+ "captions.vtt",
+ "captions.srt",
+ "transcript.txt",
+ "manifest.json",
+ "reproduction.json"
+ ],
+ "files": [
+ {
+ "file": "README.md",
+ "bytes": 3267,
+ "sha256": "f4ac5c7d681ad5a99e688da04575bf21aae2cdc1dfded4395c7a20ee3b5d07fb"
+ },
+ {
+ "file": "annotated.md",
+ "bytes": 793,
+ "sha256": "036d8e65b847fc03c68814b610be94e2b7a27573188947451348f9f674150e6e"
+ },
+ {
+ "file": "baseline-role.json",
+ "bytes": 1506,
+ "sha256": "e850422ec72faa06aae0d6c2893a9f1d2e36d60a5aef79bd8f863daa2d71b015"
+ },
+ {
+ "file": "knowledge-graph.json",
+ "bytes": 281,
+ "sha256": "f8648b16470e764bdff5c95064078419d71e8d41d2ca30d4b1508017c166a45d"
+ },
+ {
+ "file": "project-brief.md",
+ "bytes": 265,
+ "sha256": "ea78a26e4d5a1105891bed67288a924cbbd5656defc95de4c511f4302b8d0d8f"
+ },
+ {
+ "file": "without-synthesis.json",
+ "bytes": 1248,
+ "sha256": "302eb12c12e9f8a68daca280ee783c152221214969d630929d4666c5236c5365"
+ },
+ {
+ "file": "with-synthesis.json",
+ "bytes": 2288,
+ "sha256": "d6381390ae7ce09b91cf406195b01ade90a753aad72594a7c3a1676443bf5b82"
+ },
+ {
+ "file": "released-before-sharing.json",
+ "bytes": 344,
+ "sha256": "c79167288ff37166e9bb9e6c0d19bab2b86454d959db13680e3786349e58b7ae"
+ },
+ {
+ "file": "released-without-synthesis.json",
+ "bytes": 1247,
+ "sha256": "6dd67609b8443b6f750788988d1d2433b83cb8e8016654424fab07ccb073570c"
+ },
+ {
+ "file": "released-with-synthesis.json",
+ "bytes": 2151,
+ "sha256": "722bdd2b8aaf6e5f9910b8fecb84ae39ccb6d7dabd2a61a87b767e5347210352"
+ },
+ {
+ "file": "captions.vtt",
+ "bytes": 1704,
+ "sha256": "8c1c3013e992032bcc8fa93c72718303bdc9e492142feaede595c70d2292cf28"
+ },
+ {
+ "file": "captions.srt",
+ "bytes": 1744,
+ "sha256": "1842758bdc5b65be32400f47d8fb71401759f3826ecaa01621a3e5f03d084173"
+ },
+ {
+ "file": "transcript.txt",
+ "bytes": 1540,
+ "sha256": "1eb1999a04add460c1474a2889e7fb12238b0cafd07a77ef6246ad97ce07c9b4"
+ },
+ {
+ "file": "manifest.json",
+ "bytes": 4236,
+ "sha256": "af6ff6f0719b560d0eee3c71a3ba60ffed81857185ff1817f9839729ad170f9b"
+ }
+ ]
+}
diff --git a/static/demos/synonym-search/synonym-search-example.zip b/static/demos/synonym-search/synonym-search-example.zip
new file mode 100644
index 0000000..61821c0
Binary files /dev/null and b/static/demos/synonym-search/synonym-search-example.zip differ
diff --git a/themes/WarpDrive/templates/base.html b/themes/WarpDrive/templates/base.html
index e789fc1..8cdf7c9 100644
--- a/themes/WarpDrive/templates/base.html
+++ b/themes/WarpDrive/templates/base.html
@@ -35,6 +35,31 @@
{% endblock title %}
+ {% set share_title = config.title %}
+ {% set share_description = config.description %}
+ {% set share_image = config.base_url ~ "/images/terraphim_logo.png" %}
+ {% if page %}
+ {% set share_title = page.title %}
+ {% if page.description %}{% set share_description = page.description %}{% endif %}
+ {% if page.extra.social_image %}{% set share_image = config.base_url ~ page.extra.social_image %}{% endif %}
+ {% elif section %}
+ {% if section.title %}{% set share_title = section.title %}{% endif %}
+ {% if section.description %}{% set share_description = section.description %}{% endif %}
+ {% endif %}
+ {% set canonical_path = current_path | default(value="/404.html") %}
+ {% set canonical = config.base_url ~ canonical_path %}
+
+
+
+
+
+
+
+
+
+
+
+
{% if config.extra.analytics.google %}