Skip to content

[REFERENCE] Move DateTime frontend from NeoWiki core to RedHerb#777

Draft
malberts wants to merge 6 commits intomasterfrom
datetime-example
Draft

[REFERENCE] Move DateTime frontend from NeoWiki core to RedHerb#777
malberts wants to merge 6 commits intomasterfrom
datetime-example

Conversation

@malberts
Copy link
Copy Markdown
Collaborator

Reference PR. Not intended to merge.

Stacks on top of #754. Demonstrates what migrating an existing core property type into an extension looks like using the extension mechanism introduced in that PR.

Kept open while the team decides what permanent shape the "extension owns a real property type" story should take. The actual permanent forcing-function will be a Color property type in a separate PR (stacked on #754, not on this one). DateTime itself should stay in NeoWiki core long-term because:

What this PR does

Single commit on top of #754:

  • Deletes core DateTime code: DateTime.ts, dateTimeConversion.ts, the three .vue SFCs, their Vitest specs.
  • Adds the equivalent as plain-JS MW-native SFCs in tests/RedHerb/resources/, with the same runtime behaviour:
    • dateTimeConversion.js — host-TZ-aware toLocalInputValue / fromLocalInputValue / formatDateTimeForDisplay.
    • DateTimeDisplay.vue — semantic <time> element, TZ-suffixed format.
    • DateTimeInput.vuecdx-text-input with clock start-icon, live validation with host-local bound formatting (mirroring DateTimeInput: format min/max error bounds as host-local wall-clock #770).
    • DateTimeAttributesEditor.vue — Codex text inputs, cross-validation (min > max rejected), wire-format guard.
    • init.js — subscribes to mw.hook('neowiki.registration') and registers dateTime with the full validate/createPropertyDefinitionFromJson/getExampleValue implementation.
    • RedHerbFrontendHook.phpNeoWikiGetFrontendModules handler adding ext.redherb.
  • Removes DateTime exports from the public-API barrel (now that the files are gone).
  • extension.json on RedHerb gains the ext.redherb RL module with the full DateTime packageFiles set; NeoWiki core drops the cdxIconClock icon and neowiki-property-type-datetime message (still owned by core's neowiki-field-* contract, but property-type-datetime belongs with the property-type's label owner).

Status

  • make tsci green (688 tests)
  • make phpunit filter=RedHerb green
  • make cs phpcs + phpstan clean
  • Runtime-verified in the browser: DateTime property registers via the hook, bounds persist, validation renders

Why reference-only

Moving DateTime out and then immediately back would be pointless churn. The preferred path is:

  1. Merge Add JavaScript frontend extensibility #754 (core mechanism only — barrel + hook + adapter + registrar + broad public-API). No property-type migration.
  2. Merge the Color PR (permanent RedHerb example type). Stacked on Add JavaScript frontend extensibility #754 directly, not on this PR.
  3. Close this PR once the above have merged, leaving the commit SHA reachable for anyone wanting to see what a real core→extension migration looks like.

@malberts malberts mentioned this pull request Apr 24, 2026
10 tasks
malberts and others added 3 commits April 24, 2026 22:52
Split Vite entry: src/public-api.ts is now the barrel that re-exports
runtime symbols MW-native extensions can require('ext.neowiki'). It
imports src/neowiki.ts for its mount side-effects.

Memoize NeoWikiExtension.getTypeSpecificComponentRegistry() and
PropertyTypeRegistry on Neo so every caller sees the same instance —
prerequisite for the extension-registration hook being added next,
which needs to mutate the registry that Vue later receives.

For #686

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extensions can now register custom property types at runtime:

1. PHP: implement NeoWikiGetFrontendModules to append their RL module
   name to NeoWiki's module list for any page that loads ext.neowiki.
2. JS: subscribe to mw.hook('neowiki.registration') and call
   registrar.registerPropertyType({...}) with a plain-object
   PropertyTypeRegistration.

NeoWiki wraps incoming registrations via PropertyTypeAdapter into
BasePropertyType instances; FrontendRegistrar inserts them into the
memoized property-type and component registries.

Mount is deferred via queueMicrotask so extension init.js files get a
chance to subscribe before the hook fires. No consumer of the hook
exists yet — DateTime still lives in core and will move in a
follow-up commit.

For #686

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes -
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.

0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.

Bundle size impact: 240 KB -> 251 KB raw (+11 KB / +4%), 63 KB -> 66 KB
gzipped (+3 KB / +5%). Acceptable for the flexibility it provides
during alpha build-out.

Stacks on top of #754.
@malberts malberts force-pushed the frontend-extensibility branch from 933e95e to efc2dfc Compare April 24, 2026 20:54
@malberts malberts force-pushed the datetime-example branch 2 times, most recently from f7e519a to 8b53959 Compare April 24, 2026 20:57
malberts added a commit that referenced this pull request Apr 25, 2026
First extension-provided property-type frontend built on the
neowiki.registration hook surface. For #686. Stacks on #754.

This is an alternative path to PR #777, not a stack on it.
PR #777 is a reference-only example that moves the existing
core DateTime frontend into RedHerb to demonstrate the
migration path; the intent for DateTime itself is to remain
in NeoWiki core as a built-in. This PR introduces Color as a
permanent example of an extension-defined property type, i.e.
the shape any third-party extension would take, without
disturbing core's built-in types.

What it adds:

* ColorDisplay.vue renders a swatch + hex via a format-only
  check (intentionally tolerant of input-time constraints
  like allowedColors so previously-valid values keep
  rendering); falls back through I18nSlot for unparseable
  values.
* ColorInput.vue is a cdx-text-input plus a live preview
  swatch and the type's start icon. Mirrors the
  useStringValueInput convention: invalid mid-typing stays
  visible to the user but does not propagate through
  update:modelValue or getCurrentValue().
* ColorAttributesEditor.vue wraps an optional allowedColors
  palette in NeoNestedField, with a drag-reorderable list via
  useSortable and a dirty marker driven by useChangeDetection.
* RedHerbFrontendHook.php implements NeoWikiGetFrontendModules
  so the new ext.redherb ResourceLoader module loads alongside
  ext.neowiki.
* ColorProperty gains an optional allowedColors attribute
  (validated as 6-digit hex strings), with round-trip
  coverage in tests/phpunit/RedHerb/ColorPropertyTest.php.

Integration points exercised:

* mw.hook('neowiki.registration') as a second consumer
  alongside core property types.
* NeoWikiGetFrontendModules PHP hook.
* NeoWikiServices.getPropertyTypeRegistry and
  NeoWikiServices.getComponentRegistry.
* Composables: useValueValidation, useChangeDetection,
  useSortable.
* Vue components from the public-API barrel: NeoNestedField,
  I18nSlot.

NeoWikiHooksTest::testAddsCoreModuleWhenNoExtensionsHandleHook
now clearHook()'s NeoWikiGetFrontendModules so it tests what
its name claims regardless of which extensions the test
environment loads.

Known gaps:

* Persistence-side schema validation rejects extension-defined
  type names; saves through the schema editor return
  hookaborted until the JSON Schema in
  src/Persistence/MediaWiki/schemaContentSchema.json is
  loosened or built from the registry. See #779.
* Pinia store sharing not directly probed in this PR; deferred
  to a Vitest integration test in NeoWiki core.
* SchemaDeserializer / RestSchemaRepository / RestLayoutRepository
  do not have a natural fit in the Color flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
malberts and others added 2 commits April 27, 2026 11:02
Each initialize*Page() in neowiki.ts previously called createPinia() and
got its own root, so two Vue apps mounted on the same page could not
observe each other's store state. Centralize ownership of the Pinia in
NeoWikiExtension, expose getPinia(), and route every init through it.

Tested via a behavioral spec that mutates a store via one consumer and
reads it via another using the same Pinia, which is the actual contract
the shared instance is meant to guarantee.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
testAddsCoreModuleWhenNoExtensionsHandleHook asserts that only
ext.neowiki is queued when no extension contributes via the
NeoWikiGetFrontendModules hook. Real loaded extensions (such as
RedHerb in CI) register handlers that contribute their own modules,
so the assertion fails. Clear the hook at the start of the test so
its premise holds regardless of which extensions are loaded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
malberts added a commit that referenced this pull request Apr 27, 2026
First extension-provided property-type frontend built on the
neowiki.registration hook surface. For #686. Stacks on #754.

This is an alternative path to PR #777, not a stack on it.
PR #777 is a reference-only example that moves the existing
core DateTime frontend into RedHerb to demonstrate the
migration path; the intent for DateTime itself is to remain
in NeoWiki core as a built-in. This PR introduces Color as a
permanent example of an extension-defined property type, i.e.
the shape any third-party extension would take, without
disturbing core's built-in types.

What it adds:

* ColorDisplay.vue renders a swatch + hex via a format-only
  check (intentionally tolerant of input-time constraints
  like allowedColors so previously-valid values keep
  rendering); falls back through I18nSlot for unparseable
  values.
* ColorInput.vue is a cdx-text-input plus a live preview
  swatch and the type's start icon. Mirrors the
  useStringValueInput convention: invalid mid-typing stays
  visible to the user but does not propagate through
  update:modelValue or getCurrentValue().
* ColorAttributesEditor.vue wraps an optional allowedColors
  palette in NeoNestedField, with a drag-reorderable list via
  useSortable and a dirty marker driven by useChangeDetection.
* RedHerbFrontendHook.php implements NeoWikiGetFrontendModules
  so the new ext.redherb ResourceLoader module loads alongside
  ext.neowiki.
* ColorProperty gains an optional allowedColors attribute
  (validated as 6-digit hex strings), with round-trip
  coverage in tests/phpunit/RedHerb/ColorPropertyTest.php.

Integration points exercised:

* mw.hook('neowiki.registration') as a second consumer
  alongside core property types.
* NeoWikiGetFrontendModules PHP hook.
* NeoWikiServices.getPropertyTypeRegistry and
  NeoWikiServices.getComponentRegistry.
* Composables: useValueValidation, useChangeDetection,
  useSortable.
* Vue components from the public-API barrel: NeoNestedField,
  I18nSlot.

NeoWikiHooksTest::testAddsCoreModuleWhenNoExtensionsHandleHook
now clearHook()'s NeoWikiGetFrontendModules so it tests what
its name claims regardless of which extensions the test
environment loads.

Known gaps:

* Persistence-side schema validation rejects extension-defined
  type names; saves through the schema editor return
  hookaborted until the JSON Schema in
  src/Persistence/MediaWiki/schemaContentSchema.json is
  loosened or built from the registry. See #779.
* Pinia store sharing not directly probed in this PR; deferred
  to a Vitest integration test in NeoWiki core.
* SchemaDeserializer / RestSchemaRepository / RestLayoutRepository
  do not have a natural fit in the Color flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deletes the DateTime TypeScript and Vue source files (plus their specs)
from ext.neowiki. DateTime is re-introduced in RedHerb as plain JS +
MW-native .vue SFCs (no bundler, no TypeScript), registered at runtime
via mw.hook('neowiki.registration'). RedHerb's ext.redherb RL module is
added to pages that load ext.neowiki via the new
NeoWikiGetFrontendModules PHP hook, implemented by RedHerbFrontendHook.

This serves as the first end-to-end consumer of the frontend
extensibility mechanism introduced in the previous commits and proves
that external extensions can contribute property types without
touching NeoWiki's source tree or bundler.

Component-level unit tests for the three rewritten SFCs are omitted:
Vitest + @vitejs/plugin-vue expects ES modules, and RedHerb is a test
extension rather than a production code path. The validation message
key 'neowiki-field-invalid-datetime' remains in core because the
'neowiki-field-*' prefix is a documented core-owned namespace dynamically
used by every property type's validation output.

For #686

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@malberts malberts force-pushed the frontend-extensibility branch from cf6af02 to 1635dc3 Compare April 29, 2026 00:07
malberts added a commit that referenced this pull request Apr 29, 2026
First extension-provided property-type frontend built on the
neowiki.registration hook surface. For #686. Stacks on #754.

This is an alternative path to PR #777, not a stack on it.
PR #777 is a reference-only example that moves the existing
core DateTime frontend into RedHerb to demonstrate the
migration path; the intent for DateTime itself is to remain
in NeoWiki core as a built-in. This PR introduces Color as a
permanent example of an extension-defined property type, i.e.
the shape any third-party extension would take, without
disturbing core's built-in types.

What it adds:

* ColorDisplay.vue renders a swatch + hex via a format-only
  check (intentionally tolerant of input-time constraints
  like allowedColors so previously-valid values keep
  rendering); falls back through I18nSlot for unparseable
  values.
* ColorInput.vue is a cdx-text-input plus a live preview
  swatch and the type's start icon. Mirrors the
  useStringValueInput convention: invalid mid-typing stays
  visible to the user but does not propagate through
  update:modelValue or getCurrentValue().
* ColorAttributesEditor.vue wraps an optional allowedColors
  palette in NeoNestedField, with a drag-reorderable list via
  useSortable and a dirty marker driven by useChangeDetection.
* RedHerbFrontendHook.php implements NeoWikiGetFrontendModules
  so the new ext.redherb ResourceLoader module loads alongside
  ext.neowiki.
* ColorProperty gains an optional allowedColors attribute
  (validated as 6-digit hex strings), with round-trip
  coverage in tests/phpunit/RedHerb/ColorPropertyTest.php.

Integration points exercised:

* mw.hook('neowiki.registration') as a second consumer
  alongside core property types.
* NeoWikiGetFrontendModules PHP hook.
* NeoWikiServices.getPropertyTypeRegistry and
  NeoWikiServices.getComponentRegistry.
* Composables: useValueValidation, useChangeDetection,
  useSortable.
* Vue components from the public-API barrel: NeoNestedField,
  I18nSlot.

NeoWikiHooksTest::testAddsCoreModuleWhenNoExtensionsHandleHook
now clearHook()'s NeoWikiGetFrontendModules so it tests what
its name claims regardless of which extensions the test
environment loads.

Known gaps:

* Persistence-side schema validation rejects extension-defined
  type names; saves through the schema editor return
  hookaborted until the JSON Schema in
  src/Persistence/MediaWiki/schemaContentSchema.json is
  loosened or built from the registry. See #779.
* Pinia store sharing not directly probed in this PR; deferred
  to a Vitest integration test in NeoWiki core.
* SchemaDeserializer / RestSchemaRepository / RestLayoutRepository
  do not have a natural fit in the Color flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
malberts added a commit that referenced this pull request Apr 29, 2026
First extension-provided property-type frontend built on the
neowiki.registration hook surface. For #686. Stacks on #754.

This is an alternative path to PR #777, not a stack on it.
PR #777 is a reference-only example that moves the existing
core DateTime frontend into RedHerb to demonstrate the
migration path; the intent for DateTime itself is to remain
in NeoWiki core as a built-in. This PR introduces Color as a
permanent example of an extension-defined property type, i.e.
the shape any third-party extension would take, without
disturbing core's built-in types.

What it adds:

* ColorDisplay.vue renders a swatch + hex via a format-only
  check (intentionally tolerant of input-time constraints
  like allowedColors so previously-valid values keep
  rendering); falls back through I18nSlot for unparseable
  values.
* ColorInput.vue is a cdx-text-input plus a live preview
  swatch and the type's start icon. Mirrors the
  useStringValueInput convention: invalid mid-typing stays
  visible to the user but does not propagate through
  update:modelValue or getCurrentValue().
* ColorAttributesEditor.vue wraps an optional allowedColors
  palette in NeoNestedField, with a drag-reorderable list via
  useSortable and a dirty marker driven by useChangeDetection.
* RedHerbFrontendHook.php implements NeoWikiGetFrontendModules
  so the new ext.redherb ResourceLoader module loads alongside
  ext.neowiki.
* ColorProperty gains an optional allowedColors attribute
  (validated as 6-digit hex strings), with round-trip
  coverage in tests/phpunit/RedHerb/ColorPropertyTest.php.

Integration points exercised:

* mw.hook('neowiki.registration') as a second consumer
  alongside core property types.
* NeoWikiGetFrontendModules PHP hook.
* NeoWikiServices.getPropertyTypeRegistry and
  NeoWikiServices.getComponentRegistry.
* Composables: useValueValidation, useChangeDetection,
  useSortable.
* Vue components from the public-API barrel: NeoNestedField,
  I18nSlot.

NeoWikiHooksTest::testAddsCoreModuleWhenNoExtensionsHandleHook
now clearHook()'s NeoWikiGetFrontendModules so it tests what
its name claims regardless of which extensions the test
environment loads.

Known gaps:

* Persistence-side schema validation rejects extension-defined
  type names; saves through the schema editor return
  hookaborted until the JSON Schema in
  src/Persistence/MediaWiki/schemaContentSchema.json is
  loosened or built from the registry. See #779.
* Pinia store sharing not directly probed in this PR; deferred
  to a Vitest integration test in NeoWiki core.
* SchemaDeserializer / RestSchemaRepository / RestLayoutRepository
  do not have a natural fit in the Color flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
JeroenDeDauw pushed a commit that referenced this pull request Apr 29, 2026
First extension-provided property-type frontend built on the
neowiki.registration hook surface. For #686. Stacks on #754.

This is an alternative path to PR #777, not a stack on it.
PR #777 is a reference-only example that moves the existing
core DateTime frontend into RedHerb to demonstrate the
migration path; the intent for DateTime itself is to remain
in NeoWiki core as a built-in. This PR introduces Color as a
permanent example of an extension-defined property type, i.e.
the shape any third-party extension would take, without
disturbing core's built-in types.

What it adds:

* ColorDisplay.vue renders a swatch + hex via a format-only
  check (intentionally tolerant of input-time constraints
  like allowedColors so previously-valid values keep
  rendering); falls back through I18nSlot for unparseable
  values.
* ColorInput.vue is a cdx-text-input plus a live preview
  swatch and the type's start icon. Mirrors the
  useStringValueInput convention: invalid mid-typing stays
  visible to the user but does not propagate through
  update:modelValue or getCurrentValue().
* ColorAttributesEditor.vue wraps an optional allowedColors
  palette in NeoNestedField, with a drag-reorderable list via
  useSortable and a dirty marker driven by useChangeDetection.
* RedHerbFrontendHook.php implements NeoWikiGetFrontendModules
  so the new ext.redherb ResourceLoader module loads alongside
  ext.neowiki.
* ColorProperty gains an optional allowedColors attribute
  (validated as 6-digit hex strings), with round-trip
  coverage in tests/phpunit/RedHerb/ColorPropertyTest.php.

Integration points exercised:

* mw.hook('neowiki.registration') as a second consumer
  alongside core property types.
* NeoWikiGetFrontendModules PHP hook.
* NeoWikiServices.getPropertyTypeRegistry and
  NeoWikiServices.getComponentRegistry.
* Composables: useValueValidation, useChangeDetection,
  useSortable.
* Vue components from the public-API barrel: NeoNestedField,
  I18nSlot.

NeoWikiHooksTest::testAddsCoreModuleWhenNoExtensionsHandleHook
now clearHook()'s NeoWikiGetFrontendModules so it tests what
its name claims regardless of which extensions the test
environment loads.

Known gaps:

* Persistence-side schema validation rejects extension-defined
  type names; saves through the schema editor return
  hookaborted until the JSON Schema in
  src/Persistence/MediaWiki/schemaContentSchema.json is
  loosened or built from the registry. See #779.
* Pinia store sharing not directly probed in this PR; deferred
  to a Vitest integration test in NeoWiki core.
* SchemaDeserializer / RestSchemaRepository / RestLayoutRepository
  do not have a natural fit in the Color flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Base automatically changed from frontend-extensibility to master April 29, 2026 18:11
JeroenDeDauw pushed a commit that referenced this pull request Apr 29, 2026
First extension-provided property-type frontend built on the
neowiki.registration hook surface. For #686. Stacks on #754.

This is an alternative path to PR #777, not a stack on it.
PR #777 is a reference-only example that moves the existing
core DateTime frontend into RedHerb to demonstrate the
migration path; the intent for DateTime itself is to remain
in NeoWiki core as a built-in. This PR introduces Color as a
permanent example of an extension-defined property type, i.e.
the shape any third-party extension would take, without
disturbing core's built-in types.

What it adds:

* ColorDisplay.vue renders a swatch + hex via a format-only
  check (intentionally tolerant of input-time constraints
  like allowedColors so previously-valid values keep
  rendering); falls back through I18nSlot for unparseable
  values.
* ColorInput.vue is a cdx-text-input plus a live preview
  swatch and the type's start icon. Mirrors the
  useStringValueInput convention: invalid mid-typing stays
  visible to the user but does not propagate through
  update:modelValue or getCurrentValue().
* ColorAttributesEditor.vue wraps an optional allowedColors
  palette in NeoNestedField, with a drag-reorderable list via
  useSortable and a dirty marker driven by useChangeDetection.
* RedHerbFrontendHook.php implements NeoWikiGetFrontendModules
  so the new ext.redherb ResourceLoader module loads alongside
  ext.neowiki.
* ColorProperty gains an optional allowedColors attribute
  (validated as 6-digit hex strings), with round-trip
  coverage in tests/phpunit/RedHerb/ColorPropertyTest.php.

Integration points exercised:

* mw.hook('neowiki.registration') as a second consumer
  alongside core property types.
* NeoWikiGetFrontendModules PHP hook.
* NeoWikiServices.getPropertyTypeRegistry and
  NeoWikiServices.getComponentRegistry.
* Composables: useValueValidation, useChangeDetection,
  useSortable.
* Vue components from the public-API barrel: NeoNestedField,
  I18nSlot.

NeoWikiHooksTest::testAddsCoreModuleWhenNoExtensionsHandleHook
now clearHook()'s NeoWikiGetFrontendModules so it tests what
its name claims regardless of which extensions the test
environment loads.

Known gaps:

* Persistence-side schema validation rejects extension-defined
  type names; saves through the schema editor return
  hookaborted until the JSON Schema in
  src/Persistence/MediaWiki/schemaContentSchema.json is
  loosened or built from the registry. See #779.
* Pinia store sharing not directly probed in this PR; deferred
  to a Vitest integration test in NeoWiki core.
* SchemaDeserializer / RestSchemaRepository / RestLayoutRepository
  do not have a natural fit in the Color flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant