Skip to content

Commit 70babbf

Browse files
d
1 parent fa68040 commit 70babbf

11 files changed

Lines changed: 84 additions & 38 deletions

.firebaserc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"projects": {
3-
"default": "gloss-editor"
3+
"default": "holoui-editor"
44
}
55
}

.github/workflows/firebase-hosting.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ permissions:
1111

1212
env:
1313
ARCANE_JASPR_REF: 665652de05c83d51ba26da51a06398bbd1c6f57e
14-
FIREBASE_PROJECT_ID: gloss-editor
14+
FIREBASE_PROJECT_ID: holoui-editor
1515
FIREBASE_TOOLS_VERSION: 15.25.1
1616
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
17+
GLOSS_REPOSITORY: .deps/Gloss
1718
NODE_VERSION: 22.23.1
1819

1920
concurrency:
@@ -32,6 +33,13 @@ jobs:
3233
- name: Checkout repository
3334
uses: actions/checkout@v6
3435

36+
- name: Checkout Gloss runtime authority
37+
uses: actions/checkout@v6
38+
with:
39+
repository: VolmitSoftware/Gloss
40+
ref: master
41+
path: .deps/Gloss
42+
3543
- name: Cache pinned Arcane Jaspr
3644
id: cache-arcane-jaspr
3745
uses: actions/cache@v5
@@ -76,7 +84,7 @@ jobs:
7684
- name: Authenticate to Google Cloud
7785
uses: google-github-actions/auth@v3
7886
with:
79-
credentials_json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_GLOSS_EDITOR }}
87+
credentials_json: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_HOLOUI_EDITOR }}
8088

8189
- name: Deploy Firebase Hosting
8290
run: |

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# HoloUI Editor
1+
# Gloss Editor
22

3-
Visual web editor for [HoloUI](https://github.com/VolmitSoftware/HoloUi) menu configurations. Build holographic menus on a calibrated canvas, edit every field the plugin actually parses, and export `menus/<id>.json` files (plus an `images.zip`) that drop straight into `plugins/holoui/`.
3+
Visual web editor for [Gloss](https://github.com/VolmitSoftware/Gloss) menu and display configurations. Build holographic menus on a calibrated canvas, edit every field the plugin actually parses, and export documents and images that drop straight into `plugins/Gloss/`.
44

5-
Version 3.1.0 — a from-scratch rewrite in [arcane_jaspr](https://github.com/ArcaneArts/arcane_jaspr) (Dart, compiled to a fully static site). Replaces the retired Next.js editor.
5+
Version 4.0.0 — a from-scratch rewrite in [arcane_jaspr](https://github.com/ArcaneArts/arcane_jaspr) (Dart, compiled to a fully static site). Replaces the retired Next.js editor.
66

77
## Features
88

@@ -12,13 +12,13 @@ Version 3.1.0 — a from-scratch rewrite in [arcane_jaspr](https://github.com/Ar
1212
- **Full format coverage** as the plugin's Gson actually reads it — all three component types (button / decoration / toggle), all seven authorable icon types (text / textImage / animatedTextImage / item / block / customItem / entity), all six actions (command / sound / message / teleport / connect / navigate), toggle conditions, `maxDistance`, `closeOnDeath`, `closeOnTeleport`, `customModelValue` (the real key — the `customModelData` older files carry is migrated on import).
1313
- **Code view** with two-way sync, plus split view. Validated menu handoffs retain their exact source formatting and extension keys through storage and export until the first visual edit or explicit Format action.
1414
- **Validation** engine aligned with the plugin's authoring contract (lowercase registry keys, action source spellings, silent-zero pitfalls like `volume: 0`, legacy and MiniMessage formatting, hitbox overlap warnings).
15-
- **Image library**: upload validated PNG pixel art, preview with the plugin's rasterization rules, export `images.zip` laid out for `plugins/holoui/images/`. Server sync preserves captured PNG, JPEG, GIF, WebP and BMP bytes losslessly.
15+
- **Image library**: upload validated PNG pixel art, preview with the plugin's rasterization rules, export `images.zip` laid out for `plugins/Gloss/images/`. Server sync preserves captured PNG, JPEG, GIF, WebP and BMP bytes losslessly.
1616
- **Workspace**: folders, multiple menus and menu flow maps, atomic IndexedDB autosave with previous-transaction recovery and cross-tab conflict protection, undo/redo, templates, and searchable catalogs. Renaming a runtime id updates navigation, linked world-panel roots and sync scope as one checked save; dropped JSON opens as a uniquely named document instead of replacing the active one. Existing `holoui.workspace.v1` / `v2` localStorage data migrates once without deleting the rollback copy.
17-
- **Optional server sync**: capability links opened from `/holoui edit` import an exact menu or persistent world-board graph through the configured relay. Linked panels expose typed root-menu, transform, follow, audience, permission and range controls, with strict JSON under Advanced. Board publication retains the bound no-delete baseline and adds only menus reachable from the board root, leaving unrelated folder documents local. A labelled **Publish to Server** action appears in the connection bar during a live capability session; it durably saves a board project and hot-reloads the running board, while revision conflicts never overwrite local work. Links use the configured HTTPS relay (the plugin defaults to `https://sync.gloss.volmitsoftware.com/v2`) or a localhost HTTP development relay; an unreachable provider is reported without implying that a deployment is available.
17+
- **Optional server sync**: capability links opened from `/gloss menu edit` import an exact menu or persistent panel graph through the configured relay. Linked panels expose typed root-menu, transform, follow, audience, permission and range controls, with strict JSON under Advanced. Panel publication retains the bound no-delete baseline and adds only menus reachable from the panel root, leaving unrelated folder documents local. A labelled **Publish to Server** action appears in the connection bar during a live capability session; it durably saves a panel project and hot-reloads the running panel, while revision conflicts never overwrite local work. Links use the configured HTTPS relay (the plugin defaults to `https://sync.gloss.volmitsoftware.com/v2`) or a localhost HTTP development relay; an unreachable provider is reported without implying that a deployment is available.
1818

1919
The editor itself remains a static client with no accounts. Normal authoring and autosave are local-only; opening a server-issued capability link enables bounded HTTPS requests to that link's relay until the tab disconnects. Capability tokens stay in the URL fragment and tab `sessionStorage`, never in workspace documents or exported bundles. If tab storage is blocked, the capability stays in the fragment and the sync bar tells you to copy the link before reloading.
2020

21-
Sync v1 does not delete captured server resources. A board can add menus only under the displayed `newMenuPrefix`; menus and boards can add images only under `newImagePrefix`. The editor validates exact runtime ids, the whole bound graph, typed world-board JSON, image bytes, immutable constraints and content revisions before publishing. Synced raster assets are limited to 64×64 pixels each and 262,144 stored pixels per project; every repeated text-image component and animated frame is counted again against a 262,144-pixel / 4,096-row runtime-render budget. The protocol safety ceiling is 32 MiB per project; a relay deployment may enforce a lower configured limit and return `413`.
21+
Sync v2 does not delete captured server resources. A panel can add menus only under the displayed `newMenuPrefix`; menus and panels can add images only under `newImagePrefix`. The editor validates exact runtime ids, the whole bound graph, typed panel JSON, image bytes, immutable constraints and content revisions before publishing. Synced raster assets are limited to 64×64 pixels each and 262,144 stored pixels per project; every repeated text-image component and animated frame is counted again against a 262,144-pixel / 4,096-row runtime-render budget. The protocol safety ceiling is 32 MiB per project; a relay deployment may enforce a lower configured limit and return `413`.
2222

2323
Workspace bundles and server projects span IndexedDB documents and localStorage images. Import and refresh use checked compensation when either write fails, but browsers provide no atomic transaction across those two storage systems; force-closing the browser between their writes can leave a partial import.
2424

@@ -36,13 +36,13 @@ dart run jaspr_cli:jaspr build # static output in build/jaspr/
3636

3737
## Releasing
3838

39-
The hosted editor is deployed to [holoui.volmitsoftware.com](https://holoui.volmitsoftware.com/) on Firebase Hosting by `.github/workflows/firebase-hosting.yml` after every push to `master`. The workflow analyzes and tests the project before building, deploying, and verifying the custom-domain bundle. It requires the `FIREBASE_SERVICE_ACCOUNT_HOLOUI_EDITOR` repository secret. There are no editor releases: the plugin's `/holoui builder` command links players to the hosted editor.
39+
The hosted editor is deployed to [gloss.volmitsoftware.com](https://gloss.volmitsoftware.com/) through the existing Firebase `holoui-editor` project and Hosting site by `.github/workflows/firebase-hosting.yml` after every push to `master`. The workflow checks the paired Gloss runtime contract, analyzes and tests the project, then builds, deploys, and verifies the custom-domain bundle. It requires the `FIREBASE_SERVICE_ACCOUNT_HOLOUI_EDITOR` repository secret. There are no editor releases: `/gloss menu builder` links players to the hosted editor.
4040

4141

4242
## Architecture
4343

4444
```
45-
lib/model/ HoloUI JSON data model + codec (runtime semantics, unknown-key preserving)
45+
lib/model/ Gloss menu JSON data model + codec (runtime semantics, unknown-key preserving)
4646
lib/doctype/ per-kind DocumentTypeAdapter registry (codec, views, capabilities, templates, inspector)
4747
lib/logic/ validation rules, Minecraft text parser, viewport math, hitbox geometry
4848
lib/services/ browser storage, image library, catalogs, file transfer, clipboard

firebase.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"hosting": {
3-
"site": "gloss-editor",
3+
"site": "holoui-editor",
44
"public": "build/jaspr",
55
"ignore": [
66
"firebase.json",

test/emoji_catalog_test.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,25 @@ import 'package:gloss_editor/services/catalogs.dart';
1616
import 'package:gloss_editor/logic/gloss_text.dart';
1717
import 'package:test/test.dart';
1818

19-
const String _pluginDefaults = '../Gloss/src/main/resources/defaults/emoji';
19+
import 'support/gloss_repository.dart';
20+
21+
final String _pluginDefaults = glossRepositoryFilePath(
22+
'src/main/resources/defaults/emoji',
23+
);
2024
const String _asset = 'web/assets/catalog/emoji.json';
2125

2226
void main() {
23-
test('the shipped catalog matches the plugin defaults, entry for entry',
24-
() {
27+
test('the shipped catalog matches the plugin defaults, entry for entry', () {
2528
final Directory defaults = Directory(_pluginDefaults);
2629
expect(defaults.existsSync(), isTrue);
2730

2831
final Map<String, GlossEmojiDoc> expected = <String, GlossEmojiDoc>{};
2932
for (final FileSystemEntity entity in defaults.listSync()) {
3033
if (entity is! File || !entity.path.endsWith('.json')) continue;
31-
final String name = entity.uri.pathSegments.last
32-
.replaceFirst(RegExp(r'\.json$'), '');
34+
final String name = entity.uri.pathSegments.last.replaceFirst(
35+
RegExp(r'\.json$'),
36+
'',
37+
);
3338
expected[name] = decodeGlossEmojiDoc(entity.readAsStringSync());
3439
}
3540
expect(expected, isNotEmpty);

test/gloss_templates_test.dart

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import 'package:gloss_editor/logic/validation.dart';
1717
import 'package:gloss_editor/model/model.dart';
1818
import 'package:test/test.dart';
1919

20+
import 'support/gloss_repository.dart';
21+
2022
void main() {
2123
group('hologram blank stays the plugin baseline', () {
2224
test('embedded copy matches the fixture and the plugin resource', () {
@@ -25,7 +27,7 @@ void main() {
2527
).readAsStringSync();
2628
expect(kGlossHologramBaselineJson, fixture);
2729
final File plugin = File(
28-
'../Gloss/src/main/resources/baselines/hologram.json',
30+
glossRepositoryFilePath('src/main/resources/baselines/hologram.json'),
2931
);
3032
expect(plugin.existsSync(), isTrue);
3133
expect(kGlossHologramBaselineJson, plugin.readAsStringSync());
@@ -49,7 +51,9 @@ void main() {
4951
).readAsStringSync();
5052
expect(kGlossAnimationRainbowJson, fixture);
5153
final File plugin = File(
52-
'../Gloss/src/main/resources/defaults/animations/rainbow.json',
54+
glossRepositoryFilePath(
55+
'src/main/resources/defaults/animations/rainbow.json',
56+
),
5357
);
5458
expect(plugin.existsSync(), isTrue);
5559
expect(kGlossAnimationRainbowJson, plugin.readAsStringSync());
@@ -83,8 +87,9 @@ void main() {
8387
// The |animation.rainbow| line warns until the workspace has that
8488
// animation document — deliberate: the template teaches the reference.
8589
expect(
86-
issues.any((HuiIssue issue) =>
87-
issue.message.contains('animation.rainbow')),
90+
issues.any(
91+
(HuiIssue issue) => issue.message.contains('animation.rainbow'),
92+
),
8893
isTrue,
8994
);
9095
});
@@ -96,7 +101,9 @@ void main() {
96101
).readAsStringSync();
97102
expect(kGlossScoreboardDefaultJson, fixture);
98103
final File plugin = File(
99-
'../Gloss/src/main/resources/defaults/boards/default.json',
104+
glossRepositoryFilePath(
105+
'src/main/resources/defaults/boards/default.json',
106+
),
100107
);
101108
expect(plugin.existsSync(), isTrue);
102109
expect(kGlossScoreboardDefaultJson, plugin.readAsStringSync());
@@ -108,8 +115,11 @@ void main() {
108115
expect(identical(first, second), isFalse);
109116
expect(first.title, '&d&lGloss');
110117
expect(first.lines, hasLength(3));
111-
expect(first.primary, isFalse,
112-
reason: 'the shipped default never forces a sidebar');
118+
expect(
119+
first.primary,
120+
isFalse,
121+
reason: 'the shipped default never forces a sidebar',
122+
);
113123
expect(first.permission, 'default');
114124
expect(validateScoreboardDoc(first), isEmpty);
115125
});
@@ -122,7 +132,7 @@ void main() {
122132
).readAsStringSync();
123133
expect(kGlossMotdDefaultJson, fixture);
124134
final File plugin = File(
125-
'../Gloss/src/main/resources/defaults/motd/motd.json',
135+
glossRepositoryFilePath('src/main/resources/defaults/motd/motd.json'),
126136
);
127137
expect(plugin.existsSync(), isTrue);
128138
expect(kGlossMotdDefaultJson, plugin.readAsStringSync());
@@ -163,7 +173,7 @@ void main() {
163173
).readAsStringSync();
164174
expect(kGlossEmojiHeartJson, fixture);
165175
final File plugin = File(
166-
'../Gloss/src/main/resources/defaults/emoji/heart.json',
176+
glossRepositoryFilePath('src/main/resources/defaults/emoji/heart.json'),
167177
);
168178
expect(plugin.existsSync(), isTrue);
169179
expect(kGlossEmojiHeartJson, plugin.readAsStringSync());
@@ -195,7 +205,9 @@ void main() {
195205
).readAsStringSync();
196206
expect(kGlossBubbleDefaultJson, fixture);
197207
final File plugin = File(
198-
'../Gloss/src/main/resources/defaults/bubbles/default.json',
208+
glossRepositoryFilePath(
209+
'src/main/resources/defaults/bubbles/default.json',
210+
),
199211
);
200212
expect(plugin.existsSync(), isTrue);
201213
expect(kGlossBubbleDefaultJson, plugin.readAsStringSync());
@@ -222,8 +234,9 @@ void main() {
222234
expect(doc.select, isNotNull);
223235
expect(doc.select!.priority, 10);
224236
expect(
225-
validateBubbleStyleDoc(doc)
226-
.where((HuiIssue issue) => issue.severity == HuiSeverity.error),
237+
validateBubbleStyleDoc(
238+
doc,
239+
).where((HuiIssue issue) => issue.severity == HuiSeverity.error),
227240
isEmpty,
228241
);
229242
});
@@ -235,7 +248,9 @@ void main() {
235248
).readAsStringSync();
236249
expect(kGlossTablistDefaultJson, fixture);
237250
final File plugin = File(
238-
'../Gloss/src/main/resources/defaults/tablist/tablist.json',
251+
glossRepositoryFilePath(
252+
'src/main/resources/defaults/tablist/tablist.json',
253+
),
239254
);
240255
expect(plugin.existsSync(), isTrue);
241256
expect(kGlossTablistDefaultJson, plugin.readAsStringSync());

test/preview_templates_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import 'package:gloss_editor/logic/preview_variant_resolver.dart';
1414
import 'package:gloss_editor/model/preview_doc.dart';
1515
import 'package:test/test.dart';
1616

17+
import 'support/gloss_repository.dart';
18+
1719
const List<String> _shippedPreviewIds = <String>[
1820
'beehive',
1921
'brewing_stand',
@@ -138,7 +140,7 @@ void main() {
138140
expect(embedded.trim(), fixture.trim());
139141

140142
final File plugin = File(
141-
'../Gloss/src/main/resources/previews/$id.json',
143+
glossRepositoryFilePath('src/main/resources/previews/$id.json'),
142144
);
143145
expect(plugin.existsSync(), isTrue, reason: plugin.path);
144146
expect(embedded.trim(), plugin.readAsStringSync().trim());

test/support/gloss_repository.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
library;
2+
3+
import 'dart:io';
4+
5+
final String glossRepositoryPath =
6+
Platform.environment['GLOSS_REPOSITORY'] ?? '../Gloss';
7+
8+
String glossRepositoryFilePath(String relativePath) =>
9+
'$glossRepositoryPath/$relativePath';

test/templates_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import 'package:gloss_editor/logic/validation.dart';
55
import 'package:gloss_editor/model/model.dart';
66
import 'package:test/test.dart';
77

8+
import 'support/gloss_repository.dart';
9+
810
void main() {
911
test(
1012
'every starter template builds a fresh menu without validation errors',
@@ -46,7 +48,7 @@ void main() {
4648
).readAsStringSync();
4749
expect(kBlankHologramJson.trim(), fixture.trim());
4850
final File plugin = File(
49-
'../Gloss/src/main/resources/baselines/menu-blank.json',
51+
glossRepositoryFilePath('src/main/resources/baselines/menu-blank.json'),
5052
);
5153
expect(plugin.existsSync(), isTrue);
5254
expect(kBlankHologramJson.trim(), plugin.readAsStringSync().trim());

tool/extract_emoji_catalog.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import 'dart:io';
2626
import 'package:gloss_editor/model/model.dart';
2727

2828
/// Default source, relative to this repo — the plugin checkout sits beside it.
29-
const String defaultSource = '../Gloss/src/main/resources/defaults/emoji';
29+
final String defaultSource =
30+
'${Platform.environment['GLOSS_REPOSITORY'] ?? '../Gloss'}/src/main/resources/defaults/emoji';
3031

3132
const String outputPath = 'web/assets/catalog/emoji.json';
3233

@@ -42,8 +43,10 @@ void main(List<String> args) {
4243
final List<Map<String, Object?>> entries = <Map<String, Object?>>[];
4344
for (final FileSystemEntity entity in source.listSync()) {
4445
if (entity is! File || !entity.path.endsWith('.json')) continue;
45-
final String name = entity.uri.pathSegments.last
46-
.replaceFirst(RegExp(r'\.json$'), '');
46+
final String name = entity.uri.pathSegments.last.replaceFirst(
47+
RegExp(r'\.json$'),
48+
'',
49+
);
4750
final GlossEmojiDoc doc;
4851
try {
4952
doc = decodeGlossEmojiDoc(entity.readAsStringSync());

0 commit comments

Comments
 (0)