From 30c665024004bb67873212f2e9616f46fe720d05 Mon Sep 17 00:00:00 2001 From: Morne Alberts Date: Thu, 30 Apr 2026 15:24:46 +0200 Subject: [PATCH 1/3] Add RedHerb README with high-level guide for extending NeoWiki Documents the public extension points (NeoWikiRegistration hook, NeoWikiGetFrontendModules hook, neowiki.registration JS hook, public TS API, i18n convention) and points readers at the corresponding example files in this directory. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/RedHerb/README.md | 83 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 tests/RedHerb/README.md diff --git a/tests/RedHerb/README.md b/tests/RedHerb/README.md new file mode 100644 index 00000000..775e10f1 --- /dev/null +++ b/tests/RedHerb/README.md @@ -0,0 +1,83 @@ +# RedHerb + +RedHerb is a minimal MediaWiki extension that demonstrates how to extend NeoWiki. It is shipped inside the NeoWiki +repository as a live reference and is used by NeoWiki's own tests to verify that the extension points it covers work +end-to-end. RedHerb does not exercise every extension point NeoWiki exposes — it grows over time as new examples are +needed. + +If you are building a NeoWiki extension, use this directory as a working example alongside the high-level guide below. + +## Extending NeoWiki + +> **Stability:** NeoWiki is pre-1.0; everything below is alpha and may change without notice. + +### 1. `extension.json` + +A NeoWiki extension is a normal MediaWiki extension. The only NeoWiki-specific requirement is a hard dependency on +NeoWiki under `requires.extensions`. See `extension.json` for a complete example, including how to register the hook +handlers and ResourceLoader modules described below. + +### 2. Backend registration: `NeoWikiRegistration` hook + +NeoWiki fires the `NeoWikiRegistration` hook once during initialization, passing a `NeoWikiRegistrar`. This is the +single entry point for all backend registrations. Through it, an extension can: + +- **Add a Property Type** — `addPropertyType( PropertyType $type )`. Implement the `PropertyType` interface and pair + it with a class extending `PropertyDefinition` (see `src/Domain/Schema/Property/TextProperty.php` for the simplest + built-in example). +- **Add a Neo4j value builder** — `addNeo4jValueBuilder( string $propertyTypeName, callable $builder )`. Tells + NeoWiki how to convert a `NeoValue` of the given property type into the scalar(s) stored on graph nodes. Required + for any new property type whose values should be queryable in Neo4j. +- **Add a Page Property Provider** — `addPagePropertyProvider( PagePropertyProvider $provider )`. Contributes + additional key-value pairs onto `Page` nodes in the graph database. Useful for extension-specific page-level + metadata that should be queryable via Cypher. + +See `src/RedHerbHooks.php` for an example registering all three, and `src/ColorType.php`, `src/ColorProperty.php`, +and `src/StaticPagePropertyProvider.php` for the implementations they register. + +### 3. Frontend module loading: `NeoWikiGetFrontendModules` hook + +NeoWiki only loads its own ResourceLoader module (`ext.neowiki`) when its UI is rendered. To have your own RL modules +loaded alongside it (and only then), implement `NeoWikiGetFrontendModulesHook` and append your module names to +`$modules`. The `OutputPage` and `Skin` arguments are available for conditional loading. + +See `src/RedHerbFrontendHook.php`. Multiple implementations may coexist — `extension.json` registers two in RedHerb. + +### 4. Frontend registration: `mw.hook('neowiki.registration')` + +Each NeoWiki frontend mount fires the `neowiki.registration` JS hook with a `FrontendRegistrar`. Subscribe in your +module's entry point to register a Property Type frontend via `registrar.registerPropertyType( registration )`, +where `registration` matches the `PropertyTypeRegistration` TypeScript interface. + +The backend `PropertyType::getTypeName()` and the frontend `registration.typeName` must match. + +#### Vue component contracts + +The `displayComponent`, `inputComponent`, and `attributesEditor` you supply must conform to these contracts (in +NeoWiki's `resources/ext.neowiki/src/`): + +- **`displayComponent`** — `components/Value/ValueDisplayContract.ts`. +- **`inputComponent`** — `components/Value/ValueInputContract.ts`. +- **`attributesEditor`** — `components/SchemaEditor/Property/AttributesEditorContract.ts`. + +See `resources/init.js`, `ColorDisplay.vue`, `ColorInput.vue`, and `ColorAttributesEditor.vue` for working examples. + +#### Icon + +The `icon` field on the registration is a Codex icon. The default path — used by RedHerb and by every built-in +NeoWiki property type — is to pick a stock icon from +and declare it in your ResourceLoader module's `MediaWiki\ResourceLoader\CodexModule::getIcons` callback (see +RedHerb's `extension.json`). If no stock icon fits, you can supply a custom SVG by exporting it from a TypeScript +file as a string; see `resources/ext.neowiki/src/assets/CustomIcons.ts` for the pattern. + +### 5. Public frontend API: `require('ext.neowiki')` + +Your RL module should declare `ext.neowiki` as a dependency and use `require('ext.neowiki')` to access NeoWiki's +public TS API. See `resources/ext.neowiki/src/public-api.ts` for the full list of exports. + +### 6. i18n + +Standard MediaWiki i18n (`i18n/en.json`, `i18n/qqq.json`). One NeoWiki-specific convention: validation error codes +returned from a Property Type's `validate` function are resolved as `neowiki-field-` message keys, so your +extension must provide those messages for any custom codes it returns. RedHerb's `i18n/en.json` contains +`neowiki-field-invalid-hex` and `neowiki-field-not-in-palette` for this reason. From 17da343d868c75df2031e838f9a098e748c2ea90 Mon Sep 17 00:00:00 2001 From: Morne Alberts Date: Sat, 2 May 2026 00:02:19 +0200 Subject: [PATCH 2/3] Link file references in RedHerb README Makes the README's file pointers clickable in GitHub's web view, so readers can navigate to the referenced RedHerb and NeoWiki files directly. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/RedHerb/README.md | 42 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/tests/RedHerb/README.md b/tests/RedHerb/README.md index 775e10f1..e8be7664 100644 --- a/tests/RedHerb/README.md +++ b/tests/RedHerb/README.md @@ -14,8 +14,8 @@ If you are building a NeoWiki extension, use this directory as a working example ### 1. `extension.json` A NeoWiki extension is a normal MediaWiki extension. The only NeoWiki-specific requirement is a hard dependency on -NeoWiki under `requires.extensions`. See `extension.json` for a complete example, including how to register the hook -handlers and ResourceLoader modules described below. +NeoWiki under `requires.extensions`. See [`extension.json`](extension.json) for a complete example, including how to +register the hook handlers and ResourceLoader modules described below. ### 2. Backend registration: `NeoWikiRegistration` hook @@ -23,7 +23,8 @@ NeoWiki fires the `NeoWikiRegistration` hook once during initialization, passing single entry point for all backend registrations. Through it, an extension can: - **Add a Property Type** — `addPropertyType( PropertyType $type )`. Implement the `PropertyType` interface and pair - it with a class extending `PropertyDefinition` (see `src/Domain/Schema/Property/TextProperty.php` for the simplest + it with a class extending `PropertyDefinition` (see + [`src/Domain/Schema/Property/TextProperty.php`](../../src/Domain/Schema/Property/TextProperty.php) for the simplest built-in example). - **Add a Neo4j value builder** — `addNeo4jValueBuilder( string $propertyTypeName, callable $builder )`. Tells NeoWiki how to convert a `NeoValue` of the given property type into the scalar(s) stored on graph nodes. Required @@ -32,8 +33,9 @@ single entry point for all backend registrations. Through it, an extension can: additional key-value pairs onto `Page` nodes in the graph database. Useful for extension-specific page-level metadata that should be queryable via Cypher. -See `src/RedHerbHooks.php` for an example registering all three, and `src/ColorType.php`, `src/ColorProperty.php`, -and `src/StaticPagePropertyProvider.php` for the implementations they register. +See [`src/RedHerbHooks.php`](src/RedHerbHooks.php) for an example registering all three, and +[`src/ColorType.php`](src/ColorType.php), [`src/ColorProperty.php`](src/ColorProperty.php), and +[`src/StaticPagePropertyProvider.php`](src/StaticPagePropertyProvider.php) for the implementations they register. ### 3. Frontend module loading: `NeoWikiGetFrontendModules` hook @@ -41,7 +43,8 @@ NeoWiki only loads its own ResourceLoader module (`ext.neowiki`) when its UI is loaded alongside it (and only then), implement `NeoWikiGetFrontendModulesHook` and append your module names to `$modules`. The `OutputPage` and `Skin` arguments are available for conditional loading. -See `src/RedHerbFrontendHook.php`. Multiple implementations may coexist — `extension.json` registers two in RedHerb. +See [`src/RedHerbFrontendHook.php`](src/RedHerbFrontendHook.php). Multiple implementations may coexist — +[`extension.json`](extension.json) registers two in RedHerb. ### 4. Frontend registration: `mw.hook('neowiki.registration')` @@ -56,28 +59,37 @@ The backend `PropertyType::getTypeName()` and the frontend `registration.typeNam The `displayComponent`, `inputComponent`, and `attributesEditor` you supply must conform to these contracts (in NeoWiki's `resources/ext.neowiki/src/`): -- **`displayComponent`** — `components/Value/ValueDisplayContract.ts`. -- **`inputComponent`** — `components/Value/ValueInputContract.ts`. -- **`attributesEditor`** — `components/SchemaEditor/Property/AttributesEditorContract.ts`. +- **`displayComponent`** — + [`components/Value/ValueDisplayContract.ts`](../../resources/ext.neowiki/src/components/Value/ValueDisplayContract.ts). +- **`inputComponent`** — + [`components/Value/ValueInputContract.ts`](../../resources/ext.neowiki/src/components/Value/ValueInputContract.ts). +- **`attributesEditor`** — + [`components/SchemaEditor/Property/AttributesEditorContract.ts`](../../resources/ext.neowiki/src/components/SchemaEditor/Property/AttributesEditorContract.ts). -See `resources/init.js`, `ColorDisplay.vue`, `ColorInput.vue`, and `ColorAttributesEditor.vue` for working examples. +See [`resources/init.js`](resources/init.js), [`ColorDisplay.vue`](resources/ColorDisplay.vue), +[`ColorInput.vue`](resources/ColorInput.vue), and [`ColorAttributesEditor.vue`](resources/ColorAttributesEditor.vue) +for working examples. #### Icon The `icon` field on the registration is a Codex icon. The default path — used by RedHerb and by every built-in NeoWiki property type — is to pick a stock icon from and declare it in your ResourceLoader module's `MediaWiki\ResourceLoader\CodexModule::getIcons` callback (see -RedHerb's `extension.json`). If no stock icon fits, you can supply a custom SVG by exporting it from a TypeScript -file as a string; see `resources/ext.neowiki/src/assets/CustomIcons.ts` for the pattern. +RedHerb's [`extension.json`](extension.json)). If no stock icon fits, you can supply a custom SVG by exporting it +from a TypeScript file as a string; see +[`resources/ext.neowiki/src/assets/CustomIcons.ts`](../../resources/ext.neowiki/src/assets/CustomIcons.ts) for the +pattern. ### 5. Public frontend API: `require('ext.neowiki')` Your RL module should declare `ext.neowiki` as a dependency and use `require('ext.neowiki')` to access NeoWiki's -public TS API. See `resources/ext.neowiki/src/public-api.ts` for the full list of exports. +public TS API. See [`resources/ext.neowiki/src/public-api.ts`](../../resources/ext.neowiki/src/public-api.ts) for +the full list of exports. ### 6. i18n Standard MediaWiki i18n (`i18n/en.json`, `i18n/qqq.json`). One NeoWiki-specific convention: validation error codes returned from a Property Type's `validate` function are resolved as `neowiki-field-` message keys, so your -extension must provide those messages for any custom codes it returns. RedHerb's `i18n/en.json` contains -`neowiki-field-invalid-hex` and `neowiki-field-not-in-palette` for this reason. +extension must provide those messages for any custom codes it returns. RedHerb's +[`i18n/en.json`](i18n/en.json) contains `neowiki-field-invalid-hex` and `neowiki-field-not-in-palette` for this +reason. From 658a87eb42660a9f47118a3f090937eb83b0a21a Mon Sep 17 00:00:00 2001 From: Morne Alberts Date: Sat, 2 May 2026 00:08:08 +0200 Subject: [PATCH 3/3] Link second NeoWikiGetFrontendModules handler in README The README's section 3 only linked one of the two RedHerb implementations of NeoWikiGetFrontendModulesHook. Add the link to RedHerbFrontendModulesHook so both files referenced by "registers both in RedHerb" are clickable. Co-Authored-By: Claude Opus 4.7 (1M context) --- tests/RedHerb/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/RedHerb/README.md b/tests/RedHerb/README.md index e8be7664..3c8f1caa 100644 --- a/tests/RedHerb/README.md +++ b/tests/RedHerb/README.md @@ -43,8 +43,9 @@ NeoWiki only loads its own ResourceLoader module (`ext.neowiki`) when its UI is loaded alongside it (and only then), implement `NeoWikiGetFrontendModulesHook` and append your module names to `$modules`. The `OutputPage` and `Skin` arguments are available for conditional loading. -See [`src/RedHerbFrontendHook.php`](src/RedHerbFrontendHook.php). Multiple implementations may coexist — -[`extension.json`](extension.json) registers two in RedHerb. +See [`src/RedHerbFrontendHook.php`](src/RedHerbFrontendHook.php) and +[`src/RedHerbFrontendModulesHook.php`](src/RedHerbFrontendModulesHook.php). Multiple implementations may +coexist — [`extension.json`](extension.json) registers both in RedHerb. ### 4. Frontend registration: `mw.hook('neowiki.registration')`