@hai3/framework 0.2.0-alpha.0 → 0.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +31 -2
- package/commands/hai3-quick-ref.md +1 -3
- package/dist/index.cjs +400 -239
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +163 -85
- package/dist/index.d.ts +163 -85
- package/dist/index.js +389 -224
- package/dist/index.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +46 -15
- package/dist/types.d.ts +46 -15
- package/package.json +16 -5
package/dist/index.js
CHANGED
|
@@ -159,8 +159,8 @@ Add the missing plugin: .use(${depName}())`
|
|
|
159
159
|
*/
|
|
160
160
|
createStoreWithSlices(slices) {
|
|
161
161
|
const store = getStore();
|
|
162
|
-
slices.forEach((
|
|
163
|
-
registerSlice(
|
|
162
|
+
slices.forEach((slice10) => {
|
|
163
|
+
registerSlice(slice10);
|
|
164
164
|
});
|
|
165
165
|
return store;
|
|
166
166
|
}
|
|
@@ -444,6 +444,25 @@ var tenantSlice = slice8;
|
|
|
444
444
|
var tenantActions = { setTenant, setTenantLoading, clearTenant };
|
|
445
445
|
var tenantSlice_default = slice8.reducer;
|
|
446
446
|
|
|
447
|
+
// src/slices/mockSlice.ts
|
|
448
|
+
import { createSlice as createSlice9 } from "@hai3/state";
|
|
449
|
+
var SLICE_KEY9 = "mock";
|
|
450
|
+
var initialState9 = {
|
|
451
|
+
enabled: false
|
|
452
|
+
};
|
|
453
|
+
var { slice: slice9, setMockEnabled } = createSlice9({
|
|
454
|
+
name: SLICE_KEY9,
|
|
455
|
+
initialState: initialState9,
|
|
456
|
+
reducers: {
|
|
457
|
+
setMockEnabled: (state, action) => {
|
|
458
|
+
state.enabled = action.payload;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
var mockSlice = slice9;
|
|
463
|
+
var mockActions = { setMockEnabled };
|
|
464
|
+
var mockSlice_default = slice9.reducer;
|
|
465
|
+
|
|
447
466
|
// src/slices/index.ts
|
|
448
467
|
var LAYOUT_SLICE_NAME = "layout";
|
|
449
468
|
var TENANT_SLICE_NAME = "app/tenant";
|
|
@@ -489,39 +508,34 @@ function screensets(config) {
|
|
|
489
508
|
// src/plugins/themes.ts
|
|
490
509
|
import { eventBus } from "@hai3/state";
|
|
491
510
|
|
|
492
|
-
// src/compat.ts
|
|
493
|
-
import { getStore as getStore2 } from "@hai3/state";
|
|
494
|
-
import { apiRegistry as apiRegistry2 } from "@hai3/api";
|
|
495
|
-
import { screensetRegistry as sdkScreensetRegistry2 } from "@hai3/screensets";
|
|
496
|
-
|
|
497
511
|
// src/registries/themeRegistry.ts
|
|
498
|
-
function createThemeRegistry() {
|
|
512
|
+
function createThemeRegistry(config) {
|
|
499
513
|
const themes2 = /* @__PURE__ */ new Map();
|
|
500
|
-
const
|
|
514
|
+
const uikitThemes = /* @__PURE__ */ new Map();
|
|
501
515
|
let currentThemeId = null;
|
|
502
|
-
|
|
516
|
+
const customApplyFn = config?.applyFn ?? null;
|
|
503
517
|
const subscribers = /* @__PURE__ */ new Set();
|
|
504
518
|
let version = 0;
|
|
505
519
|
function notifySubscribers() {
|
|
506
520
|
version++;
|
|
507
521
|
subscribers.forEach((callback) => callback());
|
|
508
522
|
}
|
|
509
|
-
function applyCSSVariables(
|
|
523
|
+
function applyCSSVariables(config2) {
|
|
510
524
|
if (typeof document === "undefined") return;
|
|
511
525
|
const root = document.documentElement;
|
|
512
|
-
Object.entries(
|
|
526
|
+
Object.entries(config2.variables).forEach(([key, value]) => {
|
|
513
527
|
root.style.setProperty(key, value);
|
|
514
528
|
});
|
|
515
529
|
}
|
|
516
530
|
return {
|
|
517
531
|
/**
|
|
518
532
|
* Register a theme.
|
|
519
|
-
* Supports both
|
|
533
|
+
* Supports both config-based API and UIKit theme API.
|
|
520
534
|
*/
|
|
521
|
-
register(configOrId,
|
|
535
|
+
register(configOrId, uikitTheme) {
|
|
522
536
|
if (typeof configOrId === "string") {
|
|
523
537
|
const id = configOrId;
|
|
524
|
-
if (!
|
|
538
|
+
if (!uikitTheme) {
|
|
525
539
|
console.warn(`register() called with ID "${id}" but no theme object. Skipping.`);
|
|
526
540
|
return;
|
|
527
541
|
}
|
|
@@ -529,39 +543,33 @@ function createThemeRegistry() {
|
|
|
529
543
|
console.warn(`Theme "${id}" is already registered. Skipping.`);
|
|
530
544
|
return;
|
|
531
545
|
}
|
|
532
|
-
|
|
546
|
+
uikitThemes.set(id, uikitTheme);
|
|
533
547
|
let themeName = id;
|
|
534
|
-
if (
|
|
535
|
-
const nameValue =
|
|
548
|
+
if (uikitTheme && typeof uikitTheme === "object" && "name" in uikitTheme) {
|
|
549
|
+
const nameValue = uikitTheme.name;
|
|
536
550
|
if (typeof nameValue === "string") {
|
|
537
551
|
themeName = nameValue;
|
|
538
552
|
}
|
|
539
553
|
}
|
|
540
|
-
const
|
|
554
|
+
const config3 = {
|
|
541
555
|
id,
|
|
542
556
|
name: themeName,
|
|
543
557
|
variables: {}
|
|
544
|
-
//
|
|
558
|
+
// UIKit themes use custom apply function
|
|
545
559
|
};
|
|
546
|
-
themes2.set(id,
|
|
560
|
+
themes2.set(id, config3);
|
|
547
561
|
return;
|
|
548
562
|
}
|
|
549
|
-
const
|
|
550
|
-
if (themes2.has(
|
|
551
|
-
console.warn(`Theme "${
|
|
563
|
+
const config2 = configOrId;
|
|
564
|
+
if (themes2.has(config2.id)) {
|
|
565
|
+
console.warn(`Theme "${config2.id}" is already registered. Skipping.`);
|
|
552
566
|
return;
|
|
553
567
|
}
|
|
554
|
-
themes2.set(
|
|
555
|
-
if (
|
|
556
|
-
this.apply(
|
|
568
|
+
themes2.set(config2.id, config2);
|
|
569
|
+
if (config2.default && currentThemeId === null) {
|
|
570
|
+
this.apply(config2.id);
|
|
557
571
|
}
|
|
558
572
|
},
|
|
559
|
-
/**
|
|
560
|
-
* Set the apply function (legacy API).
|
|
561
|
-
*/
|
|
562
|
-
setApplyFunction(applyFn) {
|
|
563
|
-
customApplyFn = applyFn;
|
|
564
|
-
},
|
|
565
573
|
/**
|
|
566
574
|
* Get theme by ID.
|
|
567
575
|
*/
|
|
@@ -578,16 +586,17 @@ function createThemeRegistry() {
|
|
|
578
586
|
* Apply a theme.
|
|
579
587
|
*/
|
|
580
588
|
apply(id) {
|
|
581
|
-
const
|
|
582
|
-
if (!
|
|
589
|
+
const config2 = themes2.get(id);
|
|
590
|
+
if (!config2) {
|
|
583
591
|
console.warn(`Theme "${id}" not found. Cannot apply.`);
|
|
584
592
|
return;
|
|
585
593
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
594
|
+
if (config2.variables && Object.keys(config2.variables).length > 0) {
|
|
595
|
+
applyCSSVariables(config2);
|
|
596
|
+
}
|
|
597
|
+
const uikitTheme = uikitThemes.get(id);
|
|
598
|
+
if (uikitTheme && customApplyFn) {
|
|
599
|
+
customApplyFn(uikitTheme, id);
|
|
591
600
|
}
|
|
592
601
|
currentThemeId = id;
|
|
593
602
|
notifySubscribers();
|
|
@@ -618,117 +627,18 @@ function createThemeRegistry() {
|
|
|
618
627
|
};
|
|
619
628
|
}
|
|
620
629
|
|
|
621
|
-
// src/registries/routeRegistry.ts
|
|
622
|
-
function createRouteRegistry(screensetRegistry3) {
|
|
623
|
-
let routes = null;
|
|
624
|
-
function buildRoutes() {
|
|
625
|
-
if (routes !== null) {
|
|
626
|
-
return routes;
|
|
627
|
-
}
|
|
628
|
-
routes = [];
|
|
629
|
-
const screensets2 = screensetRegistry3.getAll();
|
|
630
|
-
screensets2.forEach((screenset) => {
|
|
631
|
-
screenset.menu.forEach((menuScreenItem) => {
|
|
632
|
-
const screenId = menuScreenItem.menuItem.screenId ?? menuScreenItem.menuItem.id;
|
|
633
|
-
if (screenId && menuScreenItem.screen) {
|
|
634
|
-
routes.push({
|
|
635
|
-
screensetId: screenset.id,
|
|
636
|
-
screenId,
|
|
637
|
-
loader: menuScreenItem.screen
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
});
|
|
641
|
-
});
|
|
642
|
-
return routes;
|
|
643
|
-
}
|
|
644
|
-
return {
|
|
645
|
-
/**
|
|
646
|
-
* Check if a screen exists by screenId only (globally unique).
|
|
647
|
-
*/
|
|
648
|
-
hasScreenById(screenId) {
|
|
649
|
-
const allRoutes = buildRoutes();
|
|
650
|
-
return allRoutes.some((route) => route.screenId === screenId);
|
|
651
|
-
},
|
|
652
|
-
/**
|
|
653
|
-
* Check if a screen exists (legacy, requires both IDs).
|
|
654
|
-
*/
|
|
655
|
-
hasScreen(screensetId, screenId) {
|
|
656
|
-
const allRoutes = buildRoutes();
|
|
657
|
-
return allRoutes.some(
|
|
658
|
-
(route) => route.screensetId === screensetId && route.screenId === screenId
|
|
659
|
-
);
|
|
660
|
-
},
|
|
661
|
-
/**
|
|
662
|
-
* Get screenset ID for a given screen ID (reverse lookup).
|
|
663
|
-
* Screen IDs are globally unique across all screensets.
|
|
664
|
-
*/
|
|
665
|
-
getScreensetForScreen(screenId) {
|
|
666
|
-
const allRoutes = buildRoutes();
|
|
667
|
-
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
668
|
-
return route?.screensetId;
|
|
669
|
-
},
|
|
670
|
-
/**
|
|
671
|
-
* Get screen loader by screenId only.
|
|
672
|
-
*/
|
|
673
|
-
getScreenById(screenId) {
|
|
674
|
-
const allRoutes = buildRoutes();
|
|
675
|
-
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
676
|
-
return route?.loader;
|
|
677
|
-
},
|
|
678
|
-
/**
|
|
679
|
-
* Get screen loader (legacy, requires both IDs).
|
|
680
|
-
*/
|
|
681
|
-
getScreen(screensetId, screenId) {
|
|
682
|
-
const allRoutes = buildRoutes();
|
|
683
|
-
const route = allRoutes.find(
|
|
684
|
-
(r) => r.screensetId === screensetId && r.screenId === screenId
|
|
685
|
-
);
|
|
686
|
-
return route?.loader;
|
|
687
|
-
},
|
|
688
|
-
/**
|
|
689
|
-
* Get all routes.
|
|
690
|
-
*/
|
|
691
|
-
getAll() {
|
|
692
|
-
const allRoutes = buildRoutes();
|
|
693
|
-
return allRoutes.map(({ screensetId, screenId }) => ({
|
|
694
|
-
screensetId,
|
|
695
|
-
screenId
|
|
696
|
-
}));
|
|
697
|
-
}
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// src/compat.ts
|
|
702
|
-
import { screensetRegistry } from "@hai3/screensets";
|
|
703
|
-
var screenActions3 = screenActions;
|
|
704
|
-
var ACCOUNTS_DOMAIN = "accounts";
|
|
705
|
-
var themeRegistry = createThemeRegistry();
|
|
706
|
-
var routeRegistry = createRouteRegistry(sdkScreensetRegistry2);
|
|
707
|
-
var navigateToScreen = (screenId) => {
|
|
708
|
-
getStore2().dispatch(screenActions3.setActiveScreen(screenId));
|
|
709
|
-
};
|
|
710
|
-
var fetchCurrentUser = () => (_dispatch) => {
|
|
711
|
-
try {
|
|
712
|
-
const accountsService = apiRegistry2.getService(ACCOUNTS_DOMAIN);
|
|
713
|
-
const service = accountsService;
|
|
714
|
-
service.getCurrentUser?.();
|
|
715
|
-
} catch {
|
|
716
|
-
console.warn("fetchCurrentUser: accounts service not registered");
|
|
717
|
-
}
|
|
718
|
-
};
|
|
719
|
-
|
|
720
630
|
// src/plugins/themes.ts
|
|
721
631
|
function changeTheme(payload) {
|
|
722
632
|
eventBus.emit("theme/changed", payload);
|
|
723
633
|
}
|
|
724
|
-
function themes() {
|
|
725
|
-
const
|
|
634
|
+
function themes(config) {
|
|
635
|
+
const themeRegistry = createThemeRegistry(config);
|
|
726
636
|
return {
|
|
727
637
|
name: "themes",
|
|
728
638
|
dependencies: [],
|
|
729
639
|
provides: {
|
|
730
640
|
registries: {
|
|
731
|
-
themeRegistry
|
|
641
|
+
themeRegistry
|
|
732
642
|
},
|
|
733
643
|
actions: {
|
|
734
644
|
changeTheme
|
|
@@ -736,11 +646,11 @@ function themes() {
|
|
|
736
646
|
},
|
|
737
647
|
onInit(_app) {
|
|
738
648
|
eventBus.on("theme/changed", (payload) => {
|
|
739
|
-
|
|
649
|
+
themeRegistry.apply(payload.themeId);
|
|
740
650
|
});
|
|
741
|
-
const themes2 =
|
|
651
|
+
const themes2 = themeRegistry.getAll();
|
|
742
652
|
if (themes2.length > 0) {
|
|
743
|
-
|
|
653
|
+
themeRegistry.apply(themes2[0].id);
|
|
744
654
|
}
|
|
745
655
|
}
|
|
746
656
|
};
|
|
@@ -837,60 +747,124 @@ function layout() {
|
|
|
837
747
|
// src/plugins/navigation.ts
|
|
838
748
|
import { eventBus as eventBus3 } from "@hai3/state";
|
|
839
749
|
import { i18nRegistry } from "@hai3/i18n";
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
function
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
750
|
+
|
|
751
|
+
// src/utils/basePath.ts
|
|
752
|
+
function normalizeBase(base) {
|
|
753
|
+
if (!base) {
|
|
754
|
+
return "/";
|
|
755
|
+
}
|
|
756
|
+
let normalized = base.startsWith("/") ? base : `/${base}`;
|
|
757
|
+
if (normalized !== "/" && normalized.endsWith("/")) {
|
|
758
|
+
normalized = normalized.slice(0, -1);
|
|
759
|
+
}
|
|
760
|
+
return normalized;
|
|
761
|
+
}
|
|
762
|
+
function stripBase(pathname, base) {
|
|
763
|
+
if (base === "/") {
|
|
764
|
+
return pathname;
|
|
765
|
+
}
|
|
766
|
+
if (!pathname.startsWith(base)) {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
const nextChar = pathname.charAt(base.length);
|
|
770
|
+
if (nextChar && nextChar !== "/") {
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
return pathname.slice(base.length) || "/";
|
|
848
774
|
}
|
|
849
|
-
function
|
|
775
|
+
function prependBase(path, base) {
|
|
776
|
+
if (base === "/") {
|
|
777
|
+
return path;
|
|
778
|
+
}
|
|
779
|
+
const cleanPath = path.startsWith("/") ? path : `/${path}`;
|
|
780
|
+
return `${base}${cleanPath}`;
|
|
781
|
+
}
|
|
782
|
+
function resolveBase(pluginConfig, appConfig) {
|
|
783
|
+
const rawBase = pluginConfig?.base ?? appConfig?.base ?? "/";
|
|
784
|
+
return normalizeBase(rawBase);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// src/plugins/navigation.ts
|
|
788
|
+
var screenActions3 = screenActions;
|
|
789
|
+
var menuActions3 = menuActions;
|
|
790
|
+
function navigateToScreen(payload) {
|
|
850
791
|
eventBus3.emit("navigation/screen/navigated", payload);
|
|
851
792
|
}
|
|
852
793
|
function navigateToScreenset(payload) {
|
|
853
794
|
eventBus3.emit("navigation/screenset/navigated", payload);
|
|
854
795
|
}
|
|
855
|
-
function navigation() {
|
|
796
|
+
function navigation(config) {
|
|
856
797
|
return {
|
|
857
798
|
name: "navigation",
|
|
858
799
|
dependencies: ["screensets"],
|
|
859
800
|
provides: {
|
|
860
801
|
actions: {
|
|
861
|
-
navigateToScreen
|
|
802
|
+
navigateToScreen,
|
|
862
803
|
navigateToScreenset
|
|
863
804
|
}
|
|
864
805
|
},
|
|
865
806
|
onInit(app) {
|
|
866
807
|
const dispatch = app.store.dispatch;
|
|
808
|
+
const base = resolveBase(config, app.config);
|
|
867
809
|
let currentScreensetId = null;
|
|
868
|
-
async
|
|
869
|
-
await i18nRegistry.loadScreensetTranslations(
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
810
|
+
const loadScreensetTranslations = async (screensetId, language) => {
|
|
811
|
+
await i18nRegistry.loadScreensetTranslations(
|
|
812
|
+
screensetId,
|
|
813
|
+
language
|
|
814
|
+
);
|
|
815
|
+
};
|
|
816
|
+
const updateScreensetMenu = (screenset) => {
|
|
817
|
+
const menuItems = screenset.menu.map((item) => ({
|
|
818
|
+
id: item.menuItem.screenId ?? item.menuItem.id,
|
|
819
|
+
label: item.menuItem.label,
|
|
820
|
+
icon: item.menuItem.icon
|
|
821
|
+
}));
|
|
822
|
+
dispatch(menuActions3.setMenuItems(menuItems));
|
|
823
|
+
};
|
|
824
|
+
const activateScreenset = (screensetId) => {
|
|
825
|
+
if (screensetId === currentScreensetId) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
873
828
|
const screenset = app.screensetRegistry.get(screensetId);
|
|
874
|
-
if (!screenset)
|
|
829
|
+
if (!screenset) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
875
832
|
currentScreensetId = screensetId;
|
|
876
833
|
loadScreensetTranslations(screensetId).catch((err) => {
|
|
877
834
|
console.warn(`[HAI3] Failed to load translations for screenset ${screensetId}:`, err);
|
|
878
835
|
});
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
836
|
+
updateScreensetMenu(screenset);
|
|
837
|
+
};
|
|
838
|
+
const extractScreenId = () => {
|
|
839
|
+
const internalPath = stripBase(window.location.pathname, base);
|
|
840
|
+
if (!internalPath) {
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
const parts = internalPath.split("/").filter(Boolean);
|
|
844
|
+
return parts[0] || null;
|
|
845
|
+
};
|
|
846
|
+
const activateScreen = (screenId) => {
|
|
847
|
+
const screensetId = app.routeRegistry?.getScreensetForScreen(screenId);
|
|
848
|
+
if (!screensetId) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
activateScreenset(screensetId);
|
|
852
|
+
dispatch(screenActions3.navigateTo(screenId));
|
|
853
|
+
};
|
|
882
854
|
eventBus3.on("navigation/screen/navigated", (payload) => {
|
|
883
|
-
if (
|
|
855
|
+
if (!app.routeRegistry?.hasScreen(payload.screensetId, payload.screenId)) {
|
|
884
856
|
console.warn(
|
|
885
857
|
`Screen "${payload.screenId}" in screenset "${payload.screensetId}" not found.`
|
|
886
858
|
);
|
|
887
859
|
return;
|
|
888
860
|
}
|
|
889
|
-
|
|
890
|
-
dispatch(
|
|
861
|
+
activateScreenset(payload.screensetId);
|
|
862
|
+
dispatch(screenActions3.navigateTo(payload.screenId));
|
|
891
863
|
if (typeof window !== "undefined") {
|
|
892
|
-
const url = `/${payload.screenId}
|
|
893
|
-
window.
|
|
864
|
+
const url = prependBase(`/${payload.screenId}`, base);
|
|
865
|
+
if (window.location.pathname !== url) {
|
|
866
|
+
window.history.pushState(null, "", url);
|
|
867
|
+
}
|
|
894
868
|
}
|
|
895
869
|
});
|
|
896
870
|
eventBus3.on("navigation/screenset/navigated", (payload) => {
|
|
@@ -899,7 +873,7 @@ function navigation() {
|
|
|
899
873
|
console.warn(`Screenset "${payload.screensetId}" not found.`);
|
|
900
874
|
return;
|
|
901
875
|
}
|
|
902
|
-
|
|
876
|
+
navigateToScreen({
|
|
903
877
|
screensetId: payload.screensetId,
|
|
904
878
|
screenId: screenset.defaultScreen
|
|
905
879
|
});
|
|
@@ -907,15 +881,18 @@ function navigation() {
|
|
|
907
881
|
let lastLoadedLanguage = null;
|
|
908
882
|
i18nRegistry.subscribe(() => {
|
|
909
883
|
const currentLanguage = i18nRegistry.getLanguage();
|
|
910
|
-
if (!currentLanguage || currentLanguage === lastLoadedLanguage)
|
|
911
|
-
|
|
884
|
+
if (!currentLanguage || currentLanguage === lastLoadedLanguage) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (!currentScreensetId) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
912
890
|
const screenset = app.screensetRegistry.get(currentScreensetId);
|
|
913
|
-
if (!screenset)
|
|
891
|
+
if (!screenset) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
914
894
|
lastLoadedLanguage = currentLanguage;
|
|
915
|
-
loadScreensetTranslations(currentScreensetId, currentLanguage).then(() => {
|
|
916
|
-
const menuItems = buildMenuItems(screenset);
|
|
917
|
-
dispatch(menuActions3.setMenuItems(menuItems));
|
|
918
|
-
}).catch((err) => {
|
|
895
|
+
loadScreensetTranslations(currentScreensetId, currentLanguage).then(() => updateScreensetMenu(screenset)).catch((err) => {
|
|
919
896
|
console.warn(
|
|
920
897
|
`[HAI3] Failed to reload translations for screenset ${currentScreensetId}:`,
|
|
921
898
|
err
|
|
@@ -924,31 +901,15 @@ function navigation() {
|
|
|
924
901
|
});
|
|
925
902
|
if (typeof window !== "undefined") {
|
|
926
903
|
window.addEventListener("popstate", () => {
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
const screenId = parts2[0];
|
|
931
|
-
const screensetId = app.routeRegistry?.getScreensetForScreen(screenId);
|
|
932
|
-
if (screensetId) {
|
|
933
|
-
updateMenuForScreenset(screensetId);
|
|
934
|
-
dispatch(screenActions4.navigateTo(screenId));
|
|
935
|
-
}
|
|
904
|
+
const screenId2 = extractScreenId();
|
|
905
|
+
if (screenId2) {
|
|
906
|
+
activateScreen(screenId2);
|
|
936
907
|
}
|
|
937
908
|
});
|
|
938
|
-
const
|
|
939
|
-
const parts = path.split("/").filter(Boolean);
|
|
909
|
+
const screenId = extractScreenId();
|
|
940
910
|
const autoNavigate = app.config.autoNavigate !== false;
|
|
941
|
-
if (
|
|
942
|
-
|
|
943
|
-
const screensetId = app.routeRegistry?.getScreensetForScreen(screenId);
|
|
944
|
-
if (screensetId) {
|
|
945
|
-
navigateToScreen2({ screensetId, screenId });
|
|
946
|
-
} else if (autoNavigate) {
|
|
947
|
-
const screensets2 = app.screensetRegistry.getAll();
|
|
948
|
-
if (screensets2.length > 0) {
|
|
949
|
-
navigateToScreenset({ screensetId: screensets2[0].id });
|
|
950
|
-
}
|
|
951
|
-
}
|
|
911
|
+
if (screenId) {
|
|
912
|
+
activateScreen(screenId);
|
|
952
913
|
} else if (autoNavigate) {
|
|
953
914
|
const screensets2 = app.screensetRegistry.getAll();
|
|
954
915
|
if (screensets2.length > 0) {
|
|
@@ -960,6 +921,86 @@ function navigation() {
|
|
|
960
921
|
};
|
|
961
922
|
}
|
|
962
923
|
|
|
924
|
+
// src/registries/routeRegistry.ts
|
|
925
|
+
function createRouteRegistry(screensetRegistry3) {
|
|
926
|
+
let routes = null;
|
|
927
|
+
function buildRoutes() {
|
|
928
|
+
if (routes !== null) {
|
|
929
|
+
return routes;
|
|
930
|
+
}
|
|
931
|
+
routes = [];
|
|
932
|
+
const screensets2 = screensetRegistry3.getAll();
|
|
933
|
+
screensets2.forEach((screenset) => {
|
|
934
|
+
screenset.menu.forEach((menuScreenItem) => {
|
|
935
|
+
const screenId = menuScreenItem.menuItem.screenId ?? menuScreenItem.menuItem.id;
|
|
936
|
+
if (screenId && menuScreenItem.screen) {
|
|
937
|
+
routes.push({
|
|
938
|
+
screensetId: screenset.id,
|
|
939
|
+
screenId,
|
|
940
|
+
loader: menuScreenItem.screen
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
});
|
|
945
|
+
return routes;
|
|
946
|
+
}
|
|
947
|
+
return {
|
|
948
|
+
/**
|
|
949
|
+
* Check if a screen exists by screenId only (globally unique).
|
|
950
|
+
*/
|
|
951
|
+
hasScreenById(screenId) {
|
|
952
|
+
const allRoutes = buildRoutes();
|
|
953
|
+
return allRoutes.some((route) => route.screenId === screenId);
|
|
954
|
+
},
|
|
955
|
+
/**
|
|
956
|
+
* Check if a screen exists by both screensetId and screenId (explicit lookup when screenset context is known).
|
|
957
|
+
*/
|
|
958
|
+
hasScreen(screensetId, screenId) {
|
|
959
|
+
const allRoutes = buildRoutes();
|
|
960
|
+
return allRoutes.some(
|
|
961
|
+
(route) => route.screensetId === screensetId && route.screenId === screenId
|
|
962
|
+
);
|
|
963
|
+
},
|
|
964
|
+
/**
|
|
965
|
+
* Get screenset ID for a given screen ID (reverse lookup).
|
|
966
|
+
* Screen IDs are globally unique across all screensets.
|
|
967
|
+
*/
|
|
968
|
+
getScreensetForScreen(screenId) {
|
|
969
|
+
const allRoutes = buildRoutes();
|
|
970
|
+
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
971
|
+
return route?.screensetId;
|
|
972
|
+
},
|
|
973
|
+
/**
|
|
974
|
+
* Get screen loader by screenId only.
|
|
975
|
+
*/
|
|
976
|
+
getScreenById(screenId) {
|
|
977
|
+
const allRoutes = buildRoutes();
|
|
978
|
+
const route = allRoutes.find((r) => r.screenId === screenId);
|
|
979
|
+
return route?.loader;
|
|
980
|
+
},
|
|
981
|
+
/**
|
|
982
|
+
* Get screen loader by both screensetId and screenId (explicit lookup when screenset context is known).
|
|
983
|
+
*/
|
|
984
|
+
getScreen(screensetId, screenId) {
|
|
985
|
+
const allRoutes = buildRoutes();
|
|
986
|
+
const route = allRoutes.find(
|
|
987
|
+
(r) => r.screensetId === screensetId && r.screenId === screenId
|
|
988
|
+
);
|
|
989
|
+
return route?.loader;
|
|
990
|
+
},
|
|
991
|
+
/**
|
|
992
|
+
* Get all routes.
|
|
993
|
+
*/
|
|
994
|
+
getAll() {
|
|
995
|
+
const allRoutes = buildRoutes();
|
|
996
|
+
return allRoutes.map(({ screensetId, screenId }) => ({
|
|
997
|
+
screensetId,
|
|
998
|
+
screenId
|
|
999
|
+
}));
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
|
|
963
1004
|
// src/plugins/routing.ts
|
|
964
1005
|
function routing() {
|
|
965
1006
|
return {
|
|
@@ -968,8 +1009,8 @@ function routing() {
|
|
|
968
1009
|
onRegister(_app) {
|
|
969
1010
|
},
|
|
970
1011
|
onInit(app) {
|
|
971
|
-
const
|
|
972
|
-
app.routeRegistry =
|
|
1012
|
+
const routeRegistry = createRouteRegistry(app.screensetRegistry);
|
|
1013
|
+
app.routeRegistry = routeRegistry;
|
|
973
1014
|
}
|
|
974
1015
|
};
|
|
975
1016
|
}
|
|
@@ -1017,16 +1058,101 @@ function effects() {
|
|
|
1017
1058
|
};
|
|
1018
1059
|
}
|
|
1019
1060
|
|
|
1061
|
+
// src/effects/mockEffects.ts
|
|
1062
|
+
import { eventBus as eventBus5, getStore as getStore2 } from "@hai3/state";
|
|
1063
|
+
import { apiRegistry as apiRegistry2, isMockPlugin } from "@hai3/api";
|
|
1064
|
+
function hasPluginManagement(protocol) {
|
|
1065
|
+
return "plugins" in protocol && typeof protocol.plugins === "object";
|
|
1066
|
+
}
|
|
1067
|
+
var MockEvents = {
|
|
1068
|
+
Toggle: "mock/toggle"
|
|
1069
|
+
};
|
|
1070
|
+
function syncMockPlugins(enabled) {
|
|
1071
|
+
for (const service of apiRegistry2.getAll()) {
|
|
1072
|
+
const registeredPlugins = service.getPlugins();
|
|
1073
|
+
for (const [protocol, plugins] of registeredPlugins) {
|
|
1074
|
+
if (!hasPluginManagement(protocol)) continue;
|
|
1075
|
+
for (const plugin of plugins) {
|
|
1076
|
+
if (isMockPlugin(plugin)) {
|
|
1077
|
+
if (enabled) {
|
|
1078
|
+
const existingPlugins = protocol.plugins.getAll();
|
|
1079
|
+
if (!existingPlugins.includes(plugin)) {
|
|
1080
|
+
protocol.plugins.add(plugin);
|
|
1081
|
+
}
|
|
1082
|
+
} else {
|
|
1083
|
+
protocol.plugins.remove(plugin);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
function initMockEffects() {
|
|
1091
|
+
const store = getStore2();
|
|
1092
|
+
const unsubscribe = eventBus5.on(MockEvents.Toggle, (payload) => {
|
|
1093
|
+
store.dispatch(setMockEnabled(payload.enabled));
|
|
1094
|
+
syncMockPlugins(payload.enabled);
|
|
1095
|
+
});
|
|
1096
|
+
const currentState = store.getState();
|
|
1097
|
+
if ("mock" in currentState && currentState.mock && typeof currentState.mock === "object" && "enabled" in currentState.mock) {
|
|
1098
|
+
syncMockPlugins(currentState.mock.enabled);
|
|
1099
|
+
}
|
|
1100
|
+
return () => {
|
|
1101
|
+
unsubscribe.unsubscribe();
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
function toggleMockMode(enabled) {
|
|
1105
|
+
eventBus5.emit(MockEvents.Toggle, { enabled });
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// src/plugins/mock.ts
|
|
1109
|
+
var cleanup = null;
|
|
1110
|
+
function isDevEnvironment() {
|
|
1111
|
+
if (typeof window === "undefined") return false;
|
|
1112
|
+
const { hostname } = window.location;
|
|
1113
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname.endsWith(".local");
|
|
1114
|
+
}
|
|
1115
|
+
function mock(config) {
|
|
1116
|
+
return {
|
|
1117
|
+
name: "mock",
|
|
1118
|
+
dependencies: ["effects"],
|
|
1119
|
+
provides: {
|
|
1120
|
+
slices: [mockSlice],
|
|
1121
|
+
actions: {
|
|
1122
|
+
toggleMockMode
|
|
1123
|
+
}
|
|
1124
|
+
},
|
|
1125
|
+
onInit() {
|
|
1126
|
+
cleanup = initMockEffects();
|
|
1127
|
+
const isDev = isDevEnvironment();
|
|
1128
|
+
const enabledByDefault = config?.enabledByDefault ?? isDev;
|
|
1129
|
+
if (enabledByDefault) {
|
|
1130
|
+
toggleMockMode(true);
|
|
1131
|
+
if (isDev) {
|
|
1132
|
+
console.log("[HAI3] Mock mode enabled by default (dev environment detected)");
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
onDestroy() {
|
|
1137
|
+
if (cleanup) {
|
|
1138
|
+
cleanup();
|
|
1139
|
+
cleanup = null;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1020
1145
|
// src/presets/index.ts
|
|
1021
|
-
function full() {
|
|
1146
|
+
function full(config) {
|
|
1022
1147
|
return [
|
|
1023
1148
|
effects(),
|
|
1024
1149
|
screensets({ autoDiscover: true }),
|
|
1025
|
-
themes(),
|
|
1150
|
+
themes(config?.themes),
|
|
1026
1151
|
layout(),
|
|
1027
1152
|
routing(),
|
|
1028
1153
|
navigation(),
|
|
1029
|
-
i18n()
|
|
1154
|
+
i18n(),
|
|
1155
|
+
mock()
|
|
1030
1156
|
];
|
|
1031
1157
|
}
|
|
1032
1158
|
function minimal() {
|
|
@@ -1048,31 +1174,31 @@ var presets = {
|
|
|
1048
1174
|
|
|
1049
1175
|
// src/createHAI3App.ts
|
|
1050
1176
|
function createHAI3App(config) {
|
|
1051
|
-
return createHAI3(config).useAll(full()).build();
|
|
1177
|
+
return createHAI3(config).useAll(full({ themes: config?.themes })).build();
|
|
1052
1178
|
}
|
|
1053
1179
|
|
|
1054
1180
|
// src/registries/index.ts
|
|
1055
|
-
import { createScreensetRegistry, screensetRegistry
|
|
1181
|
+
import { createScreensetRegistry, screensetRegistry } from "@hai3/screensets";
|
|
1056
1182
|
|
|
1057
1183
|
// src/index.ts
|
|
1058
|
-
import { eventBus as
|
|
1184
|
+
import { eventBus as eventBus7, createStore, getStore as getStore4, registerSlice as registerSlice2, hasSlice, createSlice as createSlice10 } from "@hai3/state";
|
|
1059
1185
|
import {
|
|
1060
1186
|
LayoutDomain,
|
|
1061
1187
|
ScreensetCategory
|
|
1062
1188
|
} from "@hai3/screensets";
|
|
1063
1189
|
|
|
1064
1190
|
// src/effects/tenantEffects.ts
|
|
1065
|
-
import { eventBus as
|
|
1191
|
+
import { eventBus as eventBus6, getStore as getStore3 } from "@hai3/state";
|
|
1066
1192
|
var TenantEvents = {
|
|
1067
1193
|
Changed: "app/tenant/changed",
|
|
1068
1194
|
Cleared: "app/tenant/cleared"
|
|
1069
1195
|
};
|
|
1070
1196
|
function initTenantEffects() {
|
|
1071
1197
|
const store = getStore3();
|
|
1072
|
-
const subChanged =
|
|
1198
|
+
const subChanged = eventBus6.on(TenantEvents.Changed, (payload) => {
|
|
1073
1199
|
store.dispatch(setTenant(payload.tenant));
|
|
1074
1200
|
});
|
|
1075
|
-
const subCleared =
|
|
1201
|
+
const subCleared = eventBus6.on(TenantEvents.Cleared, () => {
|
|
1076
1202
|
store.dispatch(clearTenant());
|
|
1077
1203
|
});
|
|
1078
1204
|
return () => {
|
|
@@ -1081,20 +1207,44 @@ function initTenantEffects() {
|
|
|
1081
1207
|
};
|
|
1082
1208
|
}
|
|
1083
1209
|
function changeTenant(tenant) {
|
|
1084
|
-
|
|
1210
|
+
eventBus6.emit(TenantEvents.Changed, { tenant });
|
|
1085
1211
|
}
|
|
1086
1212
|
function clearTenantAction() {
|
|
1087
|
-
|
|
1213
|
+
eventBus6.emit(TenantEvents.Cleared, {});
|
|
1088
1214
|
}
|
|
1089
1215
|
function setTenantLoadingState(loading) {
|
|
1090
1216
|
getStore3().dispatch(setTenantLoading(loading));
|
|
1091
1217
|
}
|
|
1092
1218
|
|
|
1093
1219
|
// src/index.ts
|
|
1094
|
-
import {
|
|
1220
|
+
import {
|
|
1221
|
+
apiRegistry as apiRegistry3,
|
|
1222
|
+
BaseApiService,
|
|
1223
|
+
RestProtocol,
|
|
1224
|
+
SseProtocol,
|
|
1225
|
+
RestMockPlugin,
|
|
1226
|
+
SseMockPlugin,
|
|
1227
|
+
MockEventSource,
|
|
1228
|
+
ApiPluginBase,
|
|
1229
|
+
ApiPlugin,
|
|
1230
|
+
ApiProtocol,
|
|
1231
|
+
RestPlugin,
|
|
1232
|
+
RestPluginWithConfig,
|
|
1233
|
+
SsePlugin,
|
|
1234
|
+
SsePluginWithConfig,
|
|
1235
|
+
isShortCircuit,
|
|
1236
|
+
isRestShortCircuit,
|
|
1237
|
+
isSseShortCircuit,
|
|
1238
|
+
MOCK_PLUGIN,
|
|
1239
|
+
isMockPlugin as isMockPlugin2
|
|
1240
|
+
} from "@hai3/api";
|
|
1095
1241
|
import { i18nRegistry as i18nRegistry2, I18nRegistryImpl, createI18nRegistry, Language as Language2, SUPPORTED_LANGUAGES, getLanguageMetadata, TextDirection, LanguageDisplayMode } from "@hai3/i18n";
|
|
1096
1242
|
import { I18nRegistryImpl as I18nRegistryImpl2 } from "@hai3/i18n";
|
|
1097
1243
|
|
|
1244
|
+
// src/compat.ts
|
|
1245
|
+
import { screensetRegistry as screensetRegistry2 } from "@hai3/screensets";
|
|
1246
|
+
var ACCOUNTS_DOMAIN = "accounts";
|
|
1247
|
+
|
|
1098
1248
|
// src/migration.ts
|
|
1099
1249
|
var STATE_PATH_MAPPING = {
|
|
1100
1250
|
// App state (moved to app slice)
|
|
@@ -1148,9 +1298,11 @@ function hasLegacyUicoreState(state) {
|
|
|
1148
1298
|
function hasNewLayoutState(state) {
|
|
1149
1299
|
return typeof state === "object" && state !== null && "layout" in state && typeof state.layout === "object";
|
|
1150
1300
|
}
|
|
1151
|
-
var legacySelectors = {};
|
|
1152
1301
|
export {
|
|
1153
1302
|
ACCOUNTS_DOMAIN,
|
|
1303
|
+
ApiPlugin,
|
|
1304
|
+
ApiPluginBase,
|
|
1305
|
+
ApiProtocol,
|
|
1154
1306
|
BaseApiService,
|
|
1155
1307
|
I18nRegistryImpl2 as I18nRegistry,
|
|
1156
1308
|
I18nRegistryImpl,
|
|
@@ -1158,11 +1310,19 @@ export {
|
|
|
1158
1310
|
Language2 as Language,
|
|
1159
1311
|
LanguageDisplayMode,
|
|
1160
1312
|
LayoutDomain,
|
|
1161
|
-
|
|
1313
|
+
MOCK_PLUGIN,
|
|
1314
|
+
MockEventSource,
|
|
1315
|
+
MockEvents,
|
|
1316
|
+
RestMockPlugin,
|
|
1317
|
+
RestPlugin,
|
|
1318
|
+
RestPluginWithConfig,
|
|
1162
1319
|
RestProtocol,
|
|
1163
1320
|
STATE_PATH_MAPPING,
|
|
1164
1321
|
SUPPORTED_LANGUAGES,
|
|
1165
1322
|
ScreensetCategory,
|
|
1323
|
+
SseMockPlugin,
|
|
1324
|
+
SsePlugin,
|
|
1325
|
+
SsePluginWithConfig,
|
|
1166
1326
|
SseProtocol,
|
|
1167
1327
|
TENANT_SLICE_NAME,
|
|
1168
1328
|
TenantEvents,
|
|
@@ -1182,12 +1342,11 @@ export {
|
|
|
1182
1342
|
createLegacySelector,
|
|
1183
1343
|
createRouteRegistry,
|
|
1184
1344
|
createScreensetRegistry,
|
|
1185
|
-
|
|
1345
|
+
createSlice10 as createSlice,
|
|
1186
1346
|
createStore,
|
|
1187
1347
|
createThemeRegistry,
|
|
1188
1348
|
effects,
|
|
1189
|
-
|
|
1190
|
-
fetchCurrentUser,
|
|
1349
|
+
eventBus7 as eventBus,
|
|
1191
1350
|
footerActions,
|
|
1192
1351
|
footerSlice,
|
|
1193
1352
|
full,
|
|
@@ -1203,17 +1362,23 @@ export {
|
|
|
1203
1362
|
hideOverlay,
|
|
1204
1363
|
i18n,
|
|
1205
1364
|
i18nRegistry2 as i18nRegistry,
|
|
1365
|
+
initMockEffects,
|
|
1206
1366
|
initTenantEffects,
|
|
1207
1367
|
isDeprecationWarningsEnabled,
|
|
1368
|
+
isMockPlugin2 as isMockPlugin,
|
|
1369
|
+
isRestShortCircuit,
|
|
1370
|
+
isShortCircuit,
|
|
1371
|
+
isSseShortCircuit,
|
|
1208
1372
|
layout,
|
|
1209
1373
|
layoutDomainReducers,
|
|
1210
1374
|
layoutReducer,
|
|
1211
|
-
legacySelectors,
|
|
1212
1375
|
menuActions,
|
|
1213
1376
|
menuSlice,
|
|
1214
1377
|
minimal,
|
|
1378
|
+
mock,
|
|
1379
|
+
mockActions,
|
|
1380
|
+
mockSlice,
|
|
1215
1381
|
navigateTo,
|
|
1216
|
-
navigateToScreen,
|
|
1217
1382
|
navigation,
|
|
1218
1383
|
openPopup,
|
|
1219
1384
|
overlayActions,
|
|
@@ -1222,11 +1387,10 @@ export {
|
|
|
1222
1387
|
popupSlice,
|
|
1223
1388
|
presets,
|
|
1224
1389
|
registerSlice2 as registerSlice,
|
|
1225
|
-
routeRegistry,
|
|
1226
1390
|
routing,
|
|
1227
1391
|
screenActions,
|
|
1228
1392
|
screenSlice,
|
|
1229
|
-
screensetRegistry,
|
|
1393
|
+
screensetRegistry2 as screensetRegistry,
|
|
1230
1394
|
screensets,
|
|
1231
1395
|
setActiveScreen,
|
|
1232
1396
|
setDeprecationWarnings,
|
|
@@ -1237,6 +1401,7 @@ export {
|
|
|
1237
1401
|
setMenuConfig,
|
|
1238
1402
|
setMenuItems,
|
|
1239
1403
|
setMenuVisible,
|
|
1404
|
+
setMockEnabled,
|
|
1240
1405
|
setOverlayVisible,
|
|
1241
1406
|
setScreenLoading,
|
|
1242
1407
|
setSidebarCollapsed,
|
|
@@ -1256,9 +1421,9 @@ export {
|
|
|
1256
1421
|
tenantActions,
|
|
1257
1422
|
tenantSlice_default as tenantReducer,
|
|
1258
1423
|
tenantSlice,
|
|
1259
|
-
themeRegistry,
|
|
1260
1424
|
themes,
|
|
1261
1425
|
toggleMenu,
|
|
1426
|
+
toggleMockMode,
|
|
1262
1427
|
toggleSidebar
|
|
1263
1428
|
};
|
|
1264
1429
|
//# sourceMappingURL=index.js.map
|