@elizaos/app-core 2.0.0-alpha.71 → 2.0.0-alpha.72

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/app-core",
3
- "version": "2.0.0-alpha.71",
3
+ "version": "2.0.0-alpha.72",
4
4
  "description": "Shared application core for Milady shells and white-label apps.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -68,8 +68,8 @@
68
68
  "@capacitor/haptics": "8.0.0",
69
69
  "@capacitor/keyboard": "8.0.0",
70
70
  "@capacitor/preferences": "^8.0.1",
71
- "@elizaos/autonomous": "2.0.0-alpha.71",
72
- "@elizaos/ui": "2.0.0-alpha.71",
71
+ "@elizaos/autonomous": "workspace:*",
72
+ "@elizaos/ui": "workspace:*",
73
73
  "@sparkjsdev/spark": "^0.1.10",
74
74
  "lucide-react": "^0.575.0",
75
75
  "three": "^0.182.0",
@@ -86,6 +86,5 @@
86
86
  "tailwindcss": "^4.1.18",
87
87
  "typescript": "^5.9.3",
88
88
  "vitest": "^4.0.18"
89
- },
90
- "gitHead": "c150a8cf4bffddd7e934f3a6073a092ca1cd6520"
89
+ }
91
90
  }
package/src/App.tsx CHANGED
@@ -219,8 +219,14 @@ export function App() {
219
219
  shellMode === "native" &&
220
220
  (isCharacterTab(effectiveTab) || isCharacterTab(tab));
221
221
  const companionShellVisible = shellMode === "companion";
222
+ // Don't initialize the 3D scene while the system is still booting — this
223
+ // prevents VrmEngine's Three.js setup from blocking the JS thread and
224
+ // delaying WebSocket agent-status updates (which would freeze the loader).
222
225
  const companionSceneActive =
223
- COMPANION_ENABLED && (companionShellVisible || characterSceneVisible);
226
+ COMPANION_ENABLED &&
227
+ !onboardingLoading &&
228
+ agentStatus?.state !== "starting" &&
229
+ (companionShellVisible || characterSceneVisible);
224
230
  const contextMenu = useContextMenu();
225
231
 
226
232
  useStreamPopoutNavigation(setTab);
@@ -57,6 +57,7 @@ export function AvatarLoader({
57
57
 
58
58
  {/* Progress bar */}
59
59
  <div
60
+ data-testid="avatar-loader-progress"
60
61
  style={{
61
62
  width: "100%",
62
63
  height: 3,
@@ -24,6 +24,7 @@ export const CHARACTER_PRESET_META: Record<
24
24
  "hehe~": { name: "Rin", avatarIndex: 5, voicePresetId: "gigi" },
25
25
  "...": { name: "Ryu", avatarIndex: 6, voicePresetId: "daniel" },
26
26
  "lmao kms": { name: "Satoshi", avatarIndex: 7, voicePresetId: "callum" },
27
+ "bruh": { name: "Yuki", avatarIndex: 8, voicePresetId: "echo" },
27
28
  };
28
29
 
29
30
  /* ── Types ────────────────────────────────────────────────────────────── */
@@ -156,6 +156,13 @@ function CompanionSceneSurface({
156
156
  companionZoomHydratedRef.current = true;
157
157
  }
158
158
 
159
+ // Lazy-mount VrmStage: only initialize the 3D engine once the scene is
160
+ // actually needed (first time active becomes true). This prevents the WebGL
161
+ // context and asset loads from firing in native/chat mode on startup.
162
+ const hasEverBeenActiveRef = useRef(active);
163
+ if (active) hasEverBeenActiveRef.current = true;
164
+ const shouldMountVrm = hasEverBeenActiveRef.current;
165
+
159
166
  const setCompanionZoom = useCallback((value: number) => {
160
167
  const nextZoom = clampCompanionZoom(value);
161
168
  companionZoomRef.current = nextZoom;
@@ -447,18 +454,20 @@ function CompanionSceneSurface({
447
454
  >
448
455
  <div className="absolute inset-0 z-0 bg-cover opacity-60 bg-[radial-gradient(circle_at_10%_20%,rgba(255,255,255,0.03)_0%,transparent_40%),radial-gradient(circle_at_80%_80%,rgba(0,225,255,0.05)_0%,transparent_40%)] pointer-events-none" />
449
456
 
450
- <VrmStage
451
- active={active}
452
- vrmPath={vrmPath}
453
- worldUrl={worldUrl}
454
- fallbackPreviewUrl={fallbackPreviewUrl}
455
- preloadAvatars={preloadAvatars}
456
- cameraProfile="companion"
457
- onEngineReady={handleStageEngineReady}
458
- onLayerEngineReady={handleStageLayerEngineReady}
459
- playWaveOnAvatarChange
460
- t={t}
461
- />
457
+ {shouldMountVrm && (
458
+ <VrmStage
459
+ active={active}
460
+ vrmPath={vrmPath}
461
+ worldUrl={worldUrl}
462
+ fallbackPreviewUrl={fallbackPreviewUrl}
463
+ preloadAvatars={preloadAvatars}
464
+ cameraProfile="companion"
465
+ onEngineReady={handleStageEngineReady}
466
+ onLayerEngineReady={handleStageLayerEngineReady}
467
+ playWaveOnAvatarChange
468
+ t={t}
469
+ />
470
+ )}
462
471
 
463
472
  <div
464
473
  aria-hidden="true"
@@ -19,14 +19,19 @@ import { OnboardingStepNav } from "./onboarding/OnboardingStepNav";
19
19
  import { PermissionsStep } from "./onboarding/PermissionsStep";
20
20
  import { RpcStep } from "./onboarding/RpcStep";
21
21
 
22
+ const FORCE_VRM =
23
+ typeof window !== "undefined" &&
24
+ new URLSearchParams(window.location.search).get("test_force_vrm") === "1";
25
+
22
26
  const DISABLE_ONBOARDING_VRM =
23
- String(import.meta.env.VITE_E2E_DISABLE_VRM ?? "").toLowerCase() === "true" ||
24
- String(import.meta.env.VITE_E2E_DISABLE_VRM ?? "") === "1";
27
+ !FORCE_VRM &&
28
+ (String(import.meta.env.VITE_E2E_DISABLE_VRM ?? "").toLowerCase() === "true" ||
29
+ String(import.meta.env.VITE_E2E_DISABLE_VRM ?? "") === "1");
25
30
 
26
31
  export function OnboardingWizard() {
27
32
  const branding = useBranding();
28
33
  const isEliza = branding.appName === "Eliza";
29
- const disableVrm = DISABLE_ONBOARDING_VRM || isEliza;
34
+ const disableVrm = !FORCE_VRM && (DISABLE_ONBOARDING_VRM || isEliza);
30
35
  const [revealStarted, setRevealStarted] = useState(disableVrm);
31
36
 
32
37
  const {
@@ -36,6 +41,7 @@ export function OnboardingWizard() {
36
41
  uiLanguage,
37
42
  uiTheme,
38
43
  setState,
44
+ handleOnboardingNext,
39
45
  t,
40
46
  } = useApp();
41
47
 
@@ -62,10 +68,25 @@ export function OnboardingWizard() {
62
68
  };
63
69
  }, [uiTheme]);
64
70
 
71
+ // Auto-advance past the wakeUp splash once the VRM reveal animation starts,
72
+ // or after a 4-second timeout to prevent getting stuck when VRM fails to load.
73
+ useEffect(() => {
74
+ if (onboardingStep !== "wakeUp") return;
75
+ if (revealStarted) {
76
+ handleOnboardingNext();
77
+ return;
78
+ }
79
+ const timer = setTimeout(() => {
80
+ setRevealStarted(true);
81
+ }, 4000);
82
+ return () => clearTimeout(timer);
83
+ }, [onboardingStep, revealStarted, handleOnboardingNext]);
84
+
65
85
  function renderStep() {
66
86
  switch (onboardingStep) {
67
87
  case "identity":
68
- return <IdentityStep />;
88
+ // Rendered outside the panel in OnboardingWizard JSX
89
+ return null;
69
90
  case "connection":
70
91
  return <ConnectionStep />;
71
92
  case "rpc":
@@ -108,6 +129,7 @@ export function OnboardingWizard() {
108
129
  )}
109
130
 
110
131
  <div
132
+ data-testid="onboarding-ui-overlay"
111
133
  style={{
112
134
  position: "absolute",
113
135
  inset: 0,
@@ -210,9 +232,16 @@ export function OnboardingWizard() {
210
232
  {/* ── Standard overlaid UI — step nav + content panel ── */}
211
233
  <div className="onboarding-ui-overlay">
212
234
  <OnboardingStepNav />
213
- <OnboardingPanel step={onboardingStep}>
214
- {renderStep()}
215
- </OnboardingPanel>
235
+ {onboardingStep === "identity" ? (
236
+ /* Identity step renders full-width at the bottom — no glass panel */
237
+ <div className="ob-identity-overlay">
238
+ <IdentityStep />
239
+ </div>
240
+ ) : (
241
+ <OnboardingPanel step={onboardingStep}>
242
+ {renderStep()}
243
+ </OnboardingPanel>
244
+ )}
216
245
  </div>
217
246
  </div>
218
247
  </div>
@@ -9,6 +9,7 @@ import { isNative } from "@elizaos/app-core/platform";
9
9
  import { getProviderLogo } from "@elizaos/app-core/providers";
10
10
  import { useApp } from "@elizaos/app-core/state";
11
11
  import { openExternalUrl } from "@elizaos/app-core/utils";
12
+ import { ONBOARDING_PROVIDER_CATALOG } from "@elizaos/autonomous/contracts/onboarding";
12
13
  import { useEffect, useState } from "react";
13
14
 
14
15
  function formatRequestError(err: unknown): string {
@@ -151,7 +152,13 @@ export function ConnectionStep() {
151
152
  setState("onboardingOpenRouterModel", modelId);
152
153
  };
153
154
 
154
- const catalogProviders = onboardingOptions?.providers ?? [];
155
+ // Use the static provider catalog shipped in the frontend bundle — no API
156
+ // call needed. Runtime overrides from `onboardingOptions` (if available)
157
+ // take precedence so the server can still augment the list when present.
158
+ const catalogProviders: ProviderOption[] =
159
+ (onboardingOptions?.providers as ProviderOption[] | undefined)?.length
160
+ ? (onboardingOptions!.providers as ProviderOption[])
161
+ : ([...ONBOARDING_PROVIDER_CATALOG] as unknown as ProviderOption[]);
155
162
  // Merge custom providers from branding (app-injected) with the catalog.
156
163
  // Custom providers are appended; duplicates (by id) are skipped.
157
164
  const customProviders = branding.customProviders ?? [];
@@ -1,29 +1,36 @@
1
- import { client } from "@elizaos/app-core/api";
2
1
  import { useApp } from "@elizaos/app-core/state";
2
+ import { getVrmPreviewUrl } from "@elizaos/app-core/state";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import {
5
- CharacterRoster,
6
5
  CHARACTER_PRESET_META,
7
- resolveRosterEntries,
8
- type CharacterRosterEntry,
6
+ SLANT_CLIP,
7
+ INSET_CLIP,
9
8
  } from "../CharacterRoster";
10
9
 
10
+ /* ── Hardcoded frontend presets — no server needed ─────────────── */
11
+
12
+ const FRONTEND_PRESETS = Object.entries(CHARACTER_PRESET_META).map(
13
+ ([catchphrase, meta]) => ({
14
+ id: catchphrase,
15
+ name: meta.name,
16
+ avatarIndex: meta.avatarIndex,
17
+ voicePresetId: meta.voicePresetId,
18
+ catchphrase,
19
+ }),
20
+ );
21
+
22
+ type PresetEntry = (typeof FRONTEND_PRESETS)[number];
23
+
11
24
  export function IdentityStep() {
12
25
  const {
13
- onboardingOptions,
14
26
  onboardingStyle,
15
27
  handleOnboardingNext,
16
28
  setState,
17
29
  t,
18
30
  } = useApp();
19
31
 
20
- const styles = onboardingOptions?.styles ?? [];
21
- const selectedCatchphrase = onboardingStyle || styles[0]?.catchphrase || "";
22
-
23
- const rosterEntries = useMemo(
24
- () => resolveRosterEntries(styles).slice(0, 4),
25
- [styles],
26
- );
32
+ const entries = FRONTEND_PRESETS;
33
+ const selectedId = onboardingStyle || entries[0]?.catchphrase || "";
27
34
 
28
35
  /* ── Import / restore state ─────────────────────────────────────── */
29
36
  const [showImport, setShowImport] = useState(false);
@@ -34,6 +41,22 @@ export function IdentityStep() {
34
41
  const [importSuccess, setImportSuccess] = useState<string | null>(null);
35
42
  const importBusyRef = useRef(false);
36
43
 
44
+ const handleSelect = useCallback(
45
+ (entry: PresetEntry) => {
46
+ setState("onboardingStyle", entry.catchphrase);
47
+ setState("onboardingName", entry.name);
48
+ setState("selectedVrmIndex", entry.avatarIndex);
49
+ },
50
+ [setState],
51
+ );
52
+
53
+ // Auto-select the first one if nothing is selected yet
54
+ useEffect(() => {
55
+ if (!onboardingStyle && entries.length > 0) {
56
+ handleSelect(entries[0]);
57
+ }
58
+ }, [onboardingStyle, entries, handleSelect]);
59
+
37
60
  const handleImportAgent = useCallback(async () => {
38
61
  if (importBusyRef.current || importBusy) return;
39
62
  if (!importFile) {
@@ -49,6 +72,8 @@ export function IdentityStep() {
49
72
  setImportBusy(true);
50
73
  setImportError(null);
51
74
  setImportSuccess(null);
75
+ // Dynamic import to avoid hard dependency on client when server is absent
76
+ const { client } = await import("@elizaos/app-core/api");
52
77
  const fileBuffer = await importFile.arrayBuffer();
53
78
  const result = await client.importAgent(importPassword, fileBuffer);
54
79
  const counts = result.counts;
@@ -75,25 +100,6 @@ export function IdentityStep() {
75
100
  }
76
101
  }, [importBusy, importFile, importPassword, t]);
77
102
 
78
- const handleSelect = useCallback(
79
- (entry: CharacterRosterEntry) => {
80
- setState("onboardingStyle", entry.id);
81
- const meta = CHARACTER_PRESET_META[entry.id];
82
- if (meta) {
83
- setState("onboardingName", meta.name);
84
- setState("selectedVrmIndex", meta.avatarIndex);
85
- }
86
- },
87
- [setState],
88
- );
89
-
90
- // Auto-select the first one if nothing is selected yet
91
- useEffect(() => {
92
- if (!onboardingStyle && rosterEntries.length > 0) {
93
- handleSelect(rosterEntries[0]);
94
- }
95
- }, [onboardingStyle, rosterEntries, handleSelect]);
96
-
97
103
  /* ── Import UI ──────────────────────────────────────────────────── */
98
104
  if (showImport) {
99
105
  return (
@@ -166,38 +172,80 @@ export function IdentityStep() {
166
172
  );
167
173
  }
168
174
 
169
- /* ── Character roster ───────────────────────────────────────────── */
175
+ /* ── Overwatch-style character select — full-width bottom bar ──── */
176
+ const selected = entries.find((e) => e.catchphrase === selectedId);
177
+
170
178
  return (
171
- <div className="flex flex-col items-center gap-4 w-full max-w-[640px]">
172
- <CharacterRoster
173
- entries={rosterEntries}
174
- selectedId={selectedCatchphrase}
175
- onSelect={handleSelect}
176
- variant="onboarding"
177
- testIdPrefix="onboarding"
178
- />
179
-
180
- <p className="onboarding-desc !mt-0 !mb-0">
181
- You can customize your character later.
182
- </p>
183
-
184
- {/* ── Next button ── */}
185
- <button
186
- className="onboarding-confirm-btn w-full max-w-[320px]"
187
- onClick={() => handleOnboardingNext()}
188
- type="button"
189
- >
190
- Next
191
- </button>
192
-
193
- {/* ── Restore from backup link ── */}
194
- <button
195
- type="button"
196
- onClick={() => setShowImport(true)}
197
- className="bg-transparent border-none text-white/40 text-xs cursor-pointer underline py-1"
198
- >
199
- {t("onboarding.restoreFromBackup")}
200
- </button>
179
+ <div className="ob-identity">
180
+ {/* Selected character info — floats above the roster */}
181
+ <div className="ob-identity-info">
182
+ <div className="ob-identity-name">{selected?.name ?? ""}</div>
183
+ </div>
184
+
185
+ {/* ── Roster bar ── */}
186
+ <div className="ob-identity-roster">
187
+ {entries.map((entry) => {
188
+ const isSelected = selectedId === entry.catchphrase;
189
+ return (
190
+ <button
191
+ key={entry.catchphrase}
192
+ type="button"
193
+ className={`ob-identity-card ${isSelected ? "ob-identity-card--active" : ""}`}
194
+ onClick={() => handleSelect(entry)}
195
+ data-testid={`onboarding-preset-${entry.catchphrase}`}
196
+ >
197
+ <div
198
+ className={`ob-identity-card-frame ${isSelected ? "ob-identity-card-frame--active" : ""}`}
199
+ style={{ clipPath: SLANT_CLIP }}
200
+ >
201
+ <div
202
+ className="ob-identity-card-inner"
203
+ style={{ clipPath: SLANT_CLIP }}
204
+ >
205
+ {isSelected && (
206
+ <div
207
+ className="pointer-events-none absolute -inset-3 bg-yellow-300/15 blur-xl"
208
+ style={{ clipPath: SLANT_CLIP }}
209
+ />
210
+ )}
211
+ <img
212
+ src={getVrmPreviewUrl(entry.avatarIndex)}
213
+ alt={entry.name}
214
+ draggable={false}
215
+ className={`ob-identity-card-img ${isSelected ? "ob-identity-card-img--active" : ""}`}
216
+ />
217
+ <div className="ob-identity-card-label">
218
+ <div
219
+ className={`ob-identity-card-name ${isSelected ? "ob-identity-card-name--active" : ""}`}
220
+ style={{ clipPath: INSET_CLIP }}
221
+ >
222
+ {entry.name}
223
+ </div>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ </button>
228
+ );
229
+ })}
230
+ </div>
231
+
232
+ {/* ── Actions row ── */}
233
+ <div className="ob-identity-actions">
234
+ <button
235
+ className="onboarding-confirm-btn"
236
+ onClick={() => handleOnboardingNext()}
237
+ type="button"
238
+ >
239
+ Continue
240
+ </button>
241
+ <button
242
+ type="button"
243
+ onClick={() => setShowImport(true)}
244
+ className="ob-identity-restore"
245
+ >
246
+ {t("onboarding.restoreFromBackup")}
247
+ </button>
248
+ </div>
201
249
  </div>
202
250
  );
203
251
  }
@@ -31,8 +31,9 @@
31
31
  .onboarding-bg-overlay {
32
32
  position: absolute;
33
33
  inset: 0;
34
- background: rgba(8, 10, 16, 0.58);
34
+ background: none;
35
35
  z-index: 1;
36
+ pointer-events: none;
36
37
  }
37
38
  @keyframes onboarding-bg-drift {
38
39
  0% {
@@ -719,6 +720,212 @@
719
720
  letter-spacing: 0.05em;
720
721
  }
721
722
 
723
+ /* === IDENTITY STEP — Overwatch-style bottom character select === */
724
+
725
+ /* Full-screen overlay container for identity step — no glass panel */
726
+ .ob-identity-overlay {
727
+ flex: 1;
728
+ display: flex;
729
+ align-items: flex-end;
730
+ justify-content: center;
731
+ padding-bottom: 0;
732
+ pointer-events: auto;
733
+ }
734
+
735
+ /* Root identity layout — stacks info, roster, actions at the bottom */
736
+ .ob-identity {
737
+ display: flex;
738
+ flex-direction: column;
739
+ align-items: center;
740
+ gap: 12px;
741
+ width: 100%;
742
+ animation: onboarding-content-fade-in 0.6s ease both;
743
+ }
744
+
745
+ /* Selected character info */
746
+ .ob-identity-info {
747
+ text-align: center;
748
+ animation: onboarding-content-fade-in 0.5s ease 0.1s both;
749
+ }
750
+
751
+ .ob-identity-name {
752
+ font-size: 28px;
753
+ font-weight: 700;
754
+ letter-spacing: 0.12em;
755
+ text-transform: uppercase;
756
+ color: rgba(240, 238, 250, 0.95);
757
+ text-shadow:
758
+ 0 0 30px rgba(240, 185, 11, 0.3),
759
+ 0 2px 12px rgba(3, 5, 10, 0.65);
760
+ transition: all 0.3s ease;
761
+ }
762
+
763
+ .ob-identity-catchphrase {
764
+ font-size: 14px;
765
+ font-style: italic;
766
+ color: rgba(240, 185, 11, 0.7);
767
+ letter-spacing: 0.05em;
768
+ margin-top: 2px;
769
+ text-shadow: 0 2px 10px rgba(3, 5, 10, 0.55);
770
+ }
771
+
772
+ .ob-identity-hint {
773
+ font-size: 12px;
774
+ color: rgba(240, 238, 250, 0.45);
775
+ margin-top: 8px;
776
+ letter-spacing: 0.04em;
777
+ }
778
+
779
+ /* ── Roster bar — horizontal row pinned to bottom ── */
780
+ .ob-identity-roster {
781
+ display: flex;
782
+ align-items: flex-end;
783
+ justify-content: center;
784
+ gap: 0;
785
+ width: 100%;
786
+ max-width: 900px;
787
+ padding: 0 2rem;
788
+ animation: ob-roster-slide-up 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.15s both;
789
+ }
790
+
791
+ @keyframes ob-roster-slide-up {
792
+ from {
793
+ opacity: 0;
794
+ transform: translateY(40px);
795
+ }
796
+ to {
797
+ opacity: 1;
798
+ transform: translateY(0);
799
+ }
800
+ }
801
+
802
+ /* Individual character card */
803
+ .ob-identity-card {
804
+ position: relative;
805
+ flex: 1 1 0;
806
+ max-width: 110px;
807
+ min-width: 0;
808
+ text-align: center;
809
+ transition: all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
810
+ cursor: pointer;
811
+ border: none;
812
+ background: none;
813
+ padding: 0;
814
+ margin: 0 -4px;
815
+ opacity: 0.5;
816
+ transform: translateY(12px);
817
+ filter: brightness(0.7);
818
+ }
819
+
820
+ .ob-identity-card:hover {
821
+ opacity: 0.85;
822
+ transform: translateY(4px);
823
+ filter: brightness(0.9);
824
+ z-index: 5;
825
+ }
826
+
827
+ .ob-identity-card--active {
828
+ opacity: 1;
829
+ z-index: 10;
830
+ transform: translateY(-8px);
831
+ filter: brightness(1);
832
+ }
833
+
834
+ .ob-identity-card--active:hover {
835
+ transform: translateY(-8px);
836
+ filter: brightness(1);
837
+ }
838
+
839
+ /* Frame border */
840
+ .ob-identity-card-frame {
841
+ position: relative;
842
+ aspect-ratio: 14 / 18;
843
+ width: 100%;
844
+ padding: 2px;
845
+ transition: all 0.25s;
846
+ background: rgba(255, 255, 255, 0.08);
847
+ }
848
+
849
+ .ob-identity-card-frame--active {
850
+ background: linear-gradient(180deg, rgba(240, 185, 11, 0.9), rgba(240, 185, 11, 0.5));
851
+ box-shadow:
852
+ 0 0 24px rgba(240, 185, 11, 0.35),
853
+ 0 4px 16px rgba(0, 0, 0, 0.3);
854
+ }
855
+
856
+ /* Inner image container */
857
+ .ob-identity-card-inner {
858
+ position: relative;
859
+ height: 100%;
860
+ width: 100%;
861
+ overflow: hidden;
862
+ background: rgba(10, 14, 20, 0.5);
863
+ }
864
+
865
+ .ob-identity-card-img {
866
+ height: 100%;
867
+ width: 100%;
868
+ object-fit: cover;
869
+ object-position: center top;
870
+ transition: transform 0.25s ease-out;
871
+ }
872
+
873
+ .ob-identity-card-img--active {
874
+ transform: scale(1.06);
875
+ }
876
+
877
+ /* Name label at bottom of card */
878
+ .ob-identity-card-label {
879
+ position: absolute;
880
+ inset-inline: 0;
881
+ bottom: 0;
882
+ }
883
+
884
+ .ob-identity-card-name {
885
+ padding: 3px 6px;
886
+ font-size: clamp(8px, 1.2vw, 11px);
887
+ font-weight: 600;
888
+ letter-spacing: 0.06em;
889
+ color: rgba(255, 255, 255, 0.8);
890
+ background: rgba(0, 0, 0, 0.55);
891
+ white-space: nowrap;
892
+ overflow: hidden;
893
+ text-overflow: ellipsis;
894
+ text-transform: uppercase;
895
+ }
896
+
897
+ .ob-identity-card-name--active {
898
+ background: rgba(0, 0, 0, 0.75);
899
+ color: rgba(240, 185, 11, 0.95);
900
+ box-shadow: inset 0 1px 0 rgba(240, 185, 11, 0.15);
901
+ }
902
+
903
+ /* ── Actions row ── */
904
+ .ob-identity-actions {
905
+ display: flex;
906
+ flex-direction: column;
907
+ align-items: center;
908
+ gap: 8px;
909
+ padding-bottom: 24px;
910
+ animation: onboarding-content-fade-in 0.5s ease 0.3s both;
911
+ }
912
+
913
+ .ob-identity-restore {
914
+ background: transparent;
915
+ border: none;
916
+ color: rgba(240, 238, 250, 0.35);
917
+ font-size: 11px;
918
+ cursor: pointer;
919
+ text-decoration: underline;
920
+ font-family: inherit;
921
+ padding: 4px 8px;
922
+ transition: color 0.3s;
923
+ }
924
+
925
+ .ob-identity-restore:hover {
926
+ color: rgba(240, 238, 250, 0.65);
927
+ }
928
+
722
929
  /* === RESPONSIVE === */
723
930
  @media (max-width: 768px) {
724
931
  .onboarding-ui-overlay {
@@ -748,4 +955,22 @@
748
955
  .onboarding-right {
749
956
  padding: 16px;
750
957
  }
958
+
959
+ /* Identity bottom bar responsive */
960
+ .ob-identity-roster {
961
+ padding: 0 0.5rem;
962
+ max-width: 100%;
963
+ }
964
+
965
+ .ob-identity-card {
966
+ max-width: 80px;
967
+ }
968
+
969
+ .ob-identity-name {
970
+ font-size: 20px;
971
+ }
972
+
973
+ .ob-identity-actions {
974
+ padding-bottom: 16px;
975
+ }
751
976
  }
@@ -1,5 +0,0 @@
1
- $ bun run build:dist
2
- $ test -f ../ui/dist/index.d.ts || (cd ../ui && bun run build) && rimraf dist && tsc -p tsconfig.build.json && node ../../scripts/copy-package-assets.mjs packages/app-core src/styles src/i18n/locales && node ../../scripts/prepare-package-dist.mjs packages/app-core
3
- error: Script not found "build"
4
- error: script "build:dist" exited with code 1
5
- error: script "build" exited with code 1
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Shaw Walters and elizaOS Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.