@bardioc/create-bardioc-app 0.5.6 → 0.6.0

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.
Files changed (65) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/templates/_base/README.md +1 -1
  4. package/templates/_base/public/app-manifest.json +1 -2
  5. package/templates/angular/.env.example +4 -1
  6. package/templates/angular/package.json +2 -2
  7. package/templates/angular/scripts/dev-env.mjs +62 -0
  8. package/templates/angular/scripts/dev.mjs +56 -0
  9. package/templates/angular/scripts/sync-runtime-env.mjs +2 -28
  10. package/templates/angular/src/app/app.component.ts +44 -26
  11. package/templates/angular/src/app/bardioc.token.ts +2 -2
  12. package/templates/angular/src/main.ts +6 -8
  13. package/templates/angular/src/styles.css +4 -0
  14. package/templates/nextjs/.env.example +4 -1
  15. package/templates/nextjs/app/page.tsx +18 -15
  16. package/templates/nextjs/next.config.ts +1 -1
  17. package/templates/nextjs/package.json +3 -3
  18. package/templates/nextjs/scripts/dev-env.mjs +62 -0
  19. package/templates/nextjs/scripts/dev.mjs +56 -0
  20. package/templates/preact/.env.example +3 -0
  21. package/templates/preact/package.json +2 -2
  22. package/templates/preact/src/App.tsx +10 -10
  23. package/templates/preact/src/main.tsx +3 -3
  24. package/templates/preact/vite.config.ts +4 -1
  25. package/templates/solid/.env.example +3 -0
  26. package/templates/solid/package.json +2 -2
  27. package/templates/solid/src/App.tsx +10 -10
  28. package/templates/solid/src/bardioc-sdk.tsx +11 -23
  29. package/templates/solid/src/main.tsx +5 -3
  30. package/templates/solid/vite.config.ts +4 -1
  31. package/templates/svelte/.env.example +3 -0
  32. package/templates/svelte/package.json +2 -2
  33. package/templates/svelte/src/App.svelte +12 -12
  34. package/templates/svelte/src/main.ts +5 -5
  35. package/templates/svelte/vite.config.ts +4 -1
  36. package/templates/vite/.env.example +3 -0
  37. package/templates/vite/CLAUDE.md +34 -7
  38. package/templates/vite/package.json +3 -3
  39. package/templates/vite/public/app-manifest.json +0 -1
  40. package/templates/vite/src/auth/onboarding.tsx +7 -5
  41. package/templates/vite/src/climate/conditions-chart.tsx +2 -2
  42. package/templates/vite/src/climate/overview-view.tsx +1 -1
  43. package/templates/vite/src/climate/stations-view.tsx +3 -10
  44. package/templates/vite/src/climate/use-climate.ts +8 -8
  45. package/templates/vite/src/events/live-events-view.tsx +8 -10
  46. package/templates/vite/src/graph/explorer-view.tsx +7 -14
  47. package/templates/vite/src/graph/requests.ts +16 -19
  48. package/templates/vite/src/locales/de.json +7 -8
  49. package/templates/vite/src/locales/en.json +45 -46
  50. package/templates/vite/src/main.tsx +3 -3
  51. package/templates/vite/src/profile/profile-view.tsx +5 -12
  52. package/templates/vite/src/profile/use-profile.ts +3 -3
  53. package/templates/vite/src/shell/about-dialog.tsx +5 -11
  54. package/templates/vite/src/shell/app-shell.tsx +63 -22
  55. package/templates/vite/src/shell/header.tsx +24 -24
  56. package/templates/vite/vite.config.ts +4 -1
  57. package/templates/vue/.env.example +3 -0
  58. package/templates/vue/package.json +2 -2
  59. package/templates/vue/src/App.vue +12 -12
  60. package/templates/vue/src/bardioc-sdk.ts +6 -15
  61. package/templates/vue/src/main.ts +5 -4
  62. package/templates/vue/vite.config.ts +4 -1
  63. package/templates/angular/scripts/dev-auth-proxy.mjs +0 -17
  64. package/templates/angular/src/app/bardioc-bridge.ts +0 -5
  65. package/templates/nextjs/scripts/dev-auth-proxy.mjs +0 -17
@@ -31,7 +31,7 @@ export function ProfileView() {
31
31
  <Empty
32
32
  icon={UserX}
33
33
  iconVariant="chip"
34
- title={t('profile.errorTitle', { fallback: 'Could not load profile' })}
34
+ title={t('profile.errorTitle', { fallback: 'Could Not Load Profile' })}
35
35
  description={profile.error ?? t('profile.errorHint', { fallback: '' })}
36
36
  />
37
37
  );
@@ -42,29 +42,22 @@ export function ProfileView() {
42
42
 
43
43
  return (
44
44
  <div className="flex flex-col gap-4">
45
- <div>
46
- <h2 className="text-lg font-semibold">
47
- {t('profile.title', { fallback: 'Operator profile' })}
48
- </h2>
49
- <p className="text-muted-foreground text-xs">
50
- {t('profile.description', { fallback: '' })}
51
- </p>
52
- </div>
45
+ <p className="text-muted-foreground text-xs">{t('profile.description', { fallback: '' })}</p>
53
46
 
54
47
  <Card>
55
48
  <div className="flex items-center gap-4">
56
49
  <Avatar firstName={name} size="lg" />
57
50
  <div className="flex min-w-0 flex-1 flex-col gap-1">
58
51
  <Row
59
- label={t('profile.displayName', { fallback: 'Display name' })}
52
+ label={t('profile.displayName', { fallback: 'Display Name' })}
60
53
  value={asString(data.display_name, unknown)}
61
54
  />
62
55
  <Row
63
- label={t('profile.firstName', { fallback: 'First name' })}
56
+ label={t('profile.firstName', { fallback: 'First Name' })}
64
57
  value={asString(data.first_name, unknown)}
65
58
  />
66
59
  <Row
67
- label={t('profile.lastName', { fallback: 'Last name' })}
60
+ label={t('profile.lastName', { fallback: 'Last Name' })}
68
61
  value={asString(data.last_name, unknown)}
69
62
  />
70
63
  </div>
@@ -25,7 +25,7 @@ export function operatorLabel(profile: Record_ | null): string | null {
25
25
  }
26
26
 
27
27
  export function useProfile(): ProfileState {
28
- const bridge = useSdk();
28
+ const bardioc = useSdk();
29
29
  const [state, setState] = useState<ProfileState>({
30
30
  loading: true,
31
31
  profile: null,
@@ -36,7 +36,7 @@ export function useProfile(): ProfileState {
36
36
  let active = true;
37
37
  void (async () => {
38
38
  try {
39
- const profile = (await bridge.transport.os.profile.get()) as Record_;
39
+ const profile = (await bardioc.os.profile.get()) as Record_;
40
40
  if (active) setState({ loading: false, profile, error: null });
41
41
  } catch (error) {
42
42
  const message = error instanceof Error ? error.message : String(error);
@@ -46,7 +46,7 @@ export function useProfile(): ProfileState {
46
46
  return () => {
47
47
  active = false;
48
48
  };
49
- }, [bridge]);
49
+ }, [bardioc]);
50
50
 
51
51
  return state;
52
52
  }
@@ -1,5 +1,5 @@
1
1
  import { BardiocLogoIcon, Dialog } from '@bardioc/ui/components';
2
- import { HashIcon, PackageIcon, ShieldCheckIcon, TagIcon } from 'lucide-react';
2
+ import { HashIcon, PackageIcon, TagIcon } from 'lucide-react';
3
3
  import { useEffect, useState, type ReactNode } from 'react';
4
4
  import { useTranslation } from '../i18n';
5
5
 
@@ -8,7 +8,6 @@ interface AppManifest {
8
8
  name: string;
9
9
  description: string;
10
10
  version: string;
11
- permissions?: string[];
12
11
  }
13
12
 
14
13
  interface AboutDialogProps {
@@ -18,7 +17,7 @@ interface AboutDialogProps {
18
17
 
19
18
  // Reads the app's own manifest (public/app-manifest.json) at runtime. The manifest is served at
20
19
  // the Vite base path, so the same code works in dev and inside the host — this is the example of
21
- // reading metadata (name, version, permissions) from the manifest instead of hard-coding it.
20
+ // reading metadata (name, version, description) from the manifest instead of hard-coding it.
22
21
  async function loadManifest(): Promise<AppManifest> {
23
22
  const response = await fetch(`${import.meta.env.BASE_URL}app-manifest.json`, {
24
23
  cache: 'no-store',
@@ -81,7 +80,7 @@ export function AboutDialog({ open, onOpenChange }: Readonly<AboutDialogProps>)
81
80
  modal
82
81
  showCloseOnHeader
83
82
  width={440}
84
- title={t('about.title', { fallback: 'About this app' })}
83
+ title={t('about.title', { fallback: 'About This App' })}
85
84
  >
86
85
  <div className="flex flex-col items-center gap-4 px-2 pb-2">
87
86
  <div className="flex flex-col items-center gap-2 py-2">
@@ -93,7 +92,7 @@ export function AboutDialog({ open, onOpenChange }: Readonly<AboutDialogProps>)
93
92
 
94
93
  {failed ? (
95
94
  <p className="text-destructive text-sm">
96
- {t('about.error', { fallback: 'Could not read app-manifest.json' })}
95
+ {t('about.error', { fallback: 'Could not read app-manifest.json.' })}
97
96
  </p>
98
97
  ) : (
99
98
  <div className="w-full">
@@ -112,16 +111,11 @@ export function AboutDialog({ open, onOpenChange }: Readonly<AboutDialogProps>)
112
111
  label={t('about.description', { fallback: 'Description' })}
113
112
  value={manifest?.description ?? dash}
114
113
  />
115
- <AboutRow
116
- icon={<ShieldCheckIcon className="size-4" />}
117
- label={t('about.permissions', { fallback: 'Permissions' })}
118
- value={manifest?.permissions?.join(', ') ?? dash}
119
- />
120
114
  </div>
121
115
  )}
122
116
 
123
117
  <p className="text-muted-foreground text-center text-xs">
124
- {t('about.source', { fallback: 'Read live from public/app-manifest.json' })}
118
+ {t('about.source', { fallback: 'Read live from public/app-manifest.json.' })}
125
119
  </p>
126
120
  </div>
127
121
  </Dialog>
@@ -1,12 +1,12 @@
1
- import type { SdkMenuRoot } from '@bardioc/app-sdk';
1
+ import type { MenuRoot } from '@bardioc/app-sdk';
2
2
  import { useCommandMenu, type AppCommand } from '@bardioc/app-sdk/react';
3
3
  import {
4
4
  BardiocLogoIcon,
5
+ Page,
5
6
  SidebarBasic,
6
7
  SidebarContainedProvider,
7
8
  Toaster,
8
9
  } from '@bardioc/ui/components';
9
- import { ScrollArea } from '@bardioc/ui/shadcn';
10
10
  import {
11
11
  Activity,
12
12
  LayoutDashboard,
@@ -28,13 +28,50 @@ import { AboutDialog } from './about-dialog';
28
28
  import { Header } from './header';
29
29
 
30
30
  // `icon` is the sidebar's React component; `iconName` is the matching lucide export name the
31
- // OS resolves for the top-menu entry (host calls resolveLucideIcon on the string).
32
- const NAV: Array<{ id: ViewId; icon: LucideIcon; iconName: string; labelKey: string }> = [
33
- { id: 'overview', icon: LayoutDashboard, iconName: 'LayoutDashboard', labelKey: 'nav.overview' },
34
- { id: 'stations', icon: Table2, iconName: 'Table2', labelKey: 'nav.stations' },
35
- { id: 'explorer', icon: Network, iconName: 'Network', labelKey: 'nav.explorer' },
36
- { id: 'events', icon: Activity, iconName: 'Activity', labelKey: 'nav.events' },
37
- { id: 'profile', icon: UserRound, iconName: 'UserRound', labelKey: 'nav.profile' },
31
+ // OS resolves for the top-menu entry (host calls resolveLucideIcon on the string). `titleKey` is
32
+ // the view's Page title, which can say more than its short navigation label.
33
+ const NAV: Array<{
34
+ id: ViewId;
35
+ icon: LucideIcon;
36
+ iconName: string;
37
+ labelKey: string;
38
+ titleKey: string;
39
+ }> = [
40
+ {
41
+ id: 'overview',
42
+ icon: LayoutDashboard,
43
+ iconName: 'LayoutDashboard',
44
+ labelKey: 'nav.overview',
45
+ titleKey: 'nav.overview',
46
+ },
47
+ {
48
+ id: 'stations',
49
+ icon: Table2,
50
+ iconName: 'Table2',
51
+ labelKey: 'nav.stations',
52
+ titleKey: 'stations.title',
53
+ },
54
+ {
55
+ id: 'explorer',
56
+ icon: Network,
57
+ iconName: 'Network',
58
+ labelKey: 'nav.explorer',
59
+ titleKey: 'explorer.title',
60
+ },
61
+ {
62
+ id: 'events',
63
+ icon: Activity,
64
+ iconName: 'Activity',
65
+ labelKey: 'nav.events',
66
+ titleKey: 'events.title',
67
+ },
68
+ {
69
+ id: 'profile',
70
+ icon: UserRound,
71
+ iconName: 'UserRound',
72
+ labelKey: 'nav.profile',
73
+ titleKey: 'profile.title',
74
+ },
38
75
  ];
39
76
 
40
77
  function ViewContent({ view }: Readonly<{ view: ViewId }>) {
@@ -68,7 +105,7 @@ export function AppShell() {
68
105
  return;
69
106
  }
70
107
  notify(
71
- t('notify.localeChanged', { fallback: 'Language switched to {locale}', params: { locale } }),
108
+ t('notify.localeChanged', { fallback: 'Language switched to {locale}.', params: { locale } }),
72
109
  'info'
73
110
  );
74
111
  }, [locale, notify, t]);
@@ -86,7 +123,7 @@ export function AppShell() {
86
123
  () => [
87
124
  {
88
125
  id: 'app.about',
89
- title: t('menu.about', { fallback: 'About this app' }),
126
+ title: t('menu.about', { fallback: 'About This App' }),
90
127
  handler: () => setAboutOpen(true),
91
128
  },
92
129
  ...NAV.map(entry => ({
@@ -99,7 +136,7 @@ export function AppShell() {
99
136
  [t, view]
100
137
  );
101
138
 
102
- const menu = useMemo<SdkMenuRoot[]>(
139
+ const menu = useMemo<MenuRoot[]>(
103
140
  () => [
104
141
  {
105
142
  label: t('app.title', { fallback: 'Weather Dashboard' }),
@@ -121,18 +158,21 @@ export function AppShell() {
121
158
 
122
159
  useCommandMenu(commands, menu);
123
160
 
161
+ const activeNav = NAV.find(entry => entry.id === view);
162
+ const pageTitle = activeNav ? t(activeNav.titleKey, { fallback: activeNav.id }) : undefined;
163
+
124
164
  const header = (
125
165
  <div className="flex items-center gap-2 px-2 py-1">
126
166
  <BardiocLogoIcon className="size-5" />
127
167
  <span className="truncate text-sm font-semibold">
128
- {t('app.title', { fallback: 'Climate Dashboard' })}
168
+ {t('app.title', { fallback: 'Weather Dashboard' })}
129
169
  </span>
130
170
  </div>
131
171
  );
132
172
 
133
173
  return (
134
174
  <SidebarContainedProvider defaultCollapsed={false}>
135
- <div className="bg-background text-foreground flex h-full min-h-0">
175
+ <div className="bg-background text-foreground @container/app flex h-full min-h-0">
136
176
  <SidebarBasic
137
177
  header={header}
138
178
  rail={false}
@@ -144,14 +184,15 @@ export function AppShell() {
144
184
  }}
145
185
  className="border-r"
146
186
  />
147
- <main className="flex min-w-0 flex-1 flex-col">
148
- <Header />
149
- <ScrollArea className="min-h-0 flex-1">
150
- <div className="p-4">
151
- <ViewContent view={view} />
152
- </div>
153
- </ScrollArea>
154
- </main>
187
+ <Page
188
+ title={pageTitle}
189
+ actions={<Header />}
190
+ actionsClassName="gap-2"
191
+ sticky
192
+ className="min-w-0"
193
+ >
194
+ <ViewContent view={view} />
195
+ </Page>
155
196
  </div>
156
197
  <AboutDialog open={aboutOpen} onOpenChange={setAboutOpen} />
157
198
  <Toaster position="bottom-right" />
@@ -4,35 +4,35 @@ import { LocationSelect } from '../climate/location-select';
4
4
  import { useDashboard } from '../dashboard-provider';
5
5
  import { SUPPORTED_LOCALES, useI18n, useTranslation } from '../i18n';
6
6
 
7
+ // Goes into Page's `actions` slot, so it renders bare controls: Page owns the header row, its
8
+ // padding and its border, and lines all three up with the body below.
7
9
  export function Header() {
8
10
  const { t } = useTranslation();
9
11
  const { state, syncing, refresh, setActiveStation } = useDashboard();
10
12
 
11
13
  return (
12
- <header className="border-border flex shrink-0 items-center justify-end gap-3 border-b px-4 py-2">
13
- <div className="flex shrink-0 items-center gap-2">
14
- {state?.stations && (
15
- <LocationSelect
16
- stations={state.stations}
17
- value={state.activeStationId}
18
- onSelect={setActiveStation}
19
- />
20
- )}
21
- <ConnectedLanguageSwitcher
22
- useI18n={useI18n}
23
- supportedLocales={[...SUPPORTED_LOCALES]}
24
- flags={{ en: 'EN', de: 'DE' }}
14
+ <>
15
+ {state?.stations && (
16
+ <LocationSelect
17
+ stations={state.stations}
18
+ value={state.activeStationId}
19
+ onSelect={setActiveStation}
25
20
  />
26
- <Button
27
- className="gap-2"
28
- size="sm"
29
- onClick={() => void refresh()}
30
- disabled={syncing || !state}
31
- >
32
- <RefreshCw className={`size-4 ${syncing ? 'animate-spin' : ''}`} />
33
- {t('header.sync', { fallback: 'Sync' })}
34
- </Button>
35
- </div>
36
- </header>
21
+ )}
22
+ <ConnectedLanguageSwitcher
23
+ useI18n={useI18n}
24
+ supportedLocales={[...SUPPORTED_LOCALES]}
25
+ flags={{ en: 'EN', de: 'DE' }}
26
+ />
27
+ <Button
28
+ className="gap-2"
29
+ size="sm"
30
+ onClick={() => void refresh()}
31
+ disabled={syncing || !state}
32
+ >
33
+ <RefreshCw className={`size-4 ${syncing ? 'animate-spin' : ''}`} />
34
+ {t('header.sync', { fallback: 'Sync' })}
35
+ </Button>
36
+ </>
37
37
  );
38
38
  }
@@ -3,9 +3,12 @@ import tailwindcss from '@tailwindcss/vite';
3
3
  import react from '@vitejs/plugin-react';
4
4
  import { defineConfig, loadEnv } from 'vite';
5
5
 
6
+ const DEFAULT_PORT = {{PORT}};
7
+
6
8
  export default defineConfig(({ mode }) => {
7
9
  const env = loadEnv(mode, __dirname, '');
8
10
  const appName = env.VITE_APP_NAME?.trim();
11
+ const port = Number(env.LOCALHOST_PORT) || DEFAULT_PORT;
9
12
 
10
13
  if (!appName) {
11
14
  throw new Error('Missing VITE_APP_NAME for Vite config');
@@ -15,7 +18,7 @@ export default defineConfig(({ mode }) => {
15
18
  plugins: [
16
19
  bardiocDevAuth({ appName }),
17
20
  react(),
18
- bardiocApp({ appName, port: {{PORT}} }),
21
+ bardiocApp({ appName, port }),
19
22
  tailwindcss(),
20
23
  ],
21
24
  };
@@ -7,3 +7,6 @@ DEV_SECRET=
7
7
 
8
8
  # Base URL of the Bardioc BFF / backend-for-frontend (e.g. https://your-host).
9
9
  API_BASE_URL=
10
+
11
+ # By default, the app will start on port {{PORT}}. Feel free to uncomment the following variable and change it to match your local setup preferences.
12
+ #LOCALHOST_PORT={{PORT}}
@@ -18,11 +18,11 @@
18
18
  "release:publish": "changeset publish"
19
19
  },
20
20
  "dependencies": {
21
- "@bardioc/app-sdk": "^0.4.9",
21
+ "@bardioc/app-sdk": "^1.0.0",
22
22
  "vue": "^3.5.16"
23
23
  },
24
24
  "devDependencies": {
25
- "@bardioc/configs": "^0.2.1",
25
+ "@bardioc/configs": "0.2.1",
26
26
  "@changesets/cli": "^2.31.0",
27
27
  "@tailwindcss/vite": "^4.3.0",
28
28
  "@types/node": "^22.19.19",
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { isDevStandalone, isInsideIframe } from '@bardioc/app-sdk/dev';
3
3
  import { ref } from 'vue';
4
- import { useBardiocBridge, useNotify } from './bardioc-sdk';
4
+ import { useBardioc, useNotify } from './bardioc-sdk';
5
5
 
6
6
  type ResultState =
7
7
  | { status: 'idle' }
@@ -10,26 +10,26 @@ type ResultState =
10
10
  | { status: 'error'; request: string; message: string };
11
11
 
12
12
  const showSdkDemo = isInsideIframe() || isDevStandalone();
13
- const bridge = useBardiocBridge();
13
+ const bardioc = useBardioc();
14
14
  const notify = useNotify();
15
15
  const result = ref<ResultState>({ status: 'idle' });
16
16
  const running = ref(false);
17
17
 
18
18
  async function runGetProfile() {
19
- if (running.value) return;
19
+ if (running.value || !bardioc) return;
20
20
  running.value = true;
21
21
 
22
- const request = 'transport.os.profile.get()';
22
+ const request = 'os.profile.get()';
23
23
  result.value = { status: 'loading', request };
24
24
 
25
25
  const startTime = Date.now();
26
- notify('Getting user profile', 'info');
26
+ notify('Getting user profile…', 'info');
27
27
 
28
28
  try {
29
- const data = await bridge.transport.os.profile.get();
29
+ const data = await bardioc.os.profile.get();
30
30
 
31
31
  const duration = Date.now() - startTime;
32
- notify(`Profile loaded (${duration}ms)`, 'success');
32
+ notify(`Profile loaded (${duration}ms).`, 'success');
33
33
  result.value = { status: 'success', request, data };
34
34
  } catch (err) {
35
35
  const message = err instanceof Error ? err.message : String(err);
@@ -41,10 +41,10 @@ async function runGetProfile() {
41
41
  }
42
42
 
43
43
  function handleNotify() {
44
- notify('Hello from {{DISPLAY_NAME}}', 'info');
44
+ notify('Hello from {{DISPLAY_NAME}}.', 'info');
45
45
  result.value = {
46
46
  status: 'success',
47
- request: 'SDK_NOTIFY',
47
+ request: 'ui.notify',
48
48
  data: { ok: true, message: 'Notification sent to OS' },
49
49
  };
50
50
  }
@@ -55,7 +55,7 @@ function clearResult() {
55
55
  </script>
56
56
 
57
57
  <template>
58
- <div class="bg-background text-foreground min-h-screen">
58
+ <div class="bg-background text-foreground @container/app min-h-screen">
59
59
  <template v-if="showSdkDemo">
60
60
  <div class="mx-auto flex min-h-screen w-full max-w-3xl flex-col gap-3 p-4">
61
61
  <div class="flex flex-col gap-1">
@@ -110,7 +110,7 @@ function clearResult() {
110
110
  <div class="text-muted-foreground border-border border-b pb-2 font-mono text-xs">
111
111
  {{ result.request }}
112
112
  </div>
113
- <div class="text-destructive break-all whitespace-pre-wrap">{{ result.message }}</div>
113
+ <div class="text-destructive whitespace-pre-wrap break-all">{{ result.message }}</div>
114
114
  </div>
115
115
  <div
116
116
  v-else-if="result.status === 'success'"
@@ -132,7 +132,7 @@ function clearResult() {
132
132
  <div class="mx-auto flex min-h-screen w-full max-w-3xl flex-col gap-4 p-4">
133
133
  <h1 class="m-0 text-2xl font-semibold">Hello Bardioc App</h1>
134
134
  <p class="text-muted-foreground m-0 max-w-2xl text-xs leading-5">
135
- Open this app inside Bardioc OS to use transport requests and send host notifications.
135
+ Open this app inside Bardioc to use transport requests and send host notifications.
136
136
  </p>
137
137
  <div class="border-border bg-muted/20 rounded-xl border p-3 text-xs leading-5">
138
138
  Outside the host iframe this page is only a local preview shell.
@@ -1,23 +1,14 @@
1
- import { createHostBridge, type HostBridge, type NotifyLevel } from '@bardioc/app-sdk';
1
+ import type { Bardioc, NotifyLevel } from '@bardioc/app-sdk';
2
2
  import { inject, type InjectionKey } from 'vue';
3
3
 
4
- export const BARDIOC_BRIDGE_KEY: InjectionKey<HostBridge> = Symbol('bardiocBridge');
4
+ export const BARDIOC_KEY: InjectionKey<Bardioc> = Symbol('bardioc');
5
5
 
6
- export function createAppSdkBridge(appId: string): HostBridge {
7
- return createHostBridge({ appId });
8
- }
9
-
10
- export function useBardiocBridge(): HostBridge {
11
- const bridge = inject(BARDIOC_BRIDGE_KEY);
12
- if (!bridge) {
13
- throw new Error('useBardiocBridge() must be used after providing BARDIOC_BRIDGE_KEY');
14
- }
15
- return bridge;
16
- }
6
+ /** The host connection, or null while the app runs outside Bardioc. */
7
+ export const useBardioc = (): Bardioc | null => inject(BARDIOC_KEY, null);
17
8
 
18
9
  export function useNotify() {
19
- const bridge = useBardiocBridge();
10
+ const bardioc = useBardioc();
20
11
  return (message: string, level?: NotifyLevel) => {
21
- bridge.notify(message, level);
12
+ bardioc?.ui.notify(message, level).catch(console.error);
22
13
  };
23
14
  }
@@ -1,7 +1,8 @@
1
- import { installDevBridge, isDevStandalone, isInsideIframe } from '@bardioc/app-sdk/dev';
1
+ import { connect } from '@bardioc/app-sdk';
2
+ import { installDevHost, isDevStandalone, isInsideIframe } from '@bardioc/app-sdk/dev';
2
3
  import { createApp } from 'vue';
3
4
  import App from './App.vue';
4
- import { BARDIOC_BRIDGE_KEY, createAppSdkBridge } from './bardioc-sdk';
5
+ import { BARDIOC_KEY } from './bardioc-sdk';
5
6
  import './index.css';
6
7
 
7
8
  const APP_NAME = import.meta.env.VITE_APP_NAME?.trim();
@@ -15,13 +16,13 @@ const devStandalone = isDevStandalone();
15
16
  const useSdkProvider = insideIframe || devStandalone;
16
17
 
17
18
  if (devStandalone) {
18
- installDevBridge({ debug: false });
19
+ installDevHost({ appId: APP_NAME });
19
20
  }
20
21
 
21
22
  const app = createApp(App);
22
23
 
23
24
  if (useSdkProvider) {
24
- app.provide(BARDIOC_BRIDGE_KEY, createAppSdkBridge(APP_NAME));
25
+ app.provide(BARDIOC_KEY, await connect());
25
26
  }
26
27
 
27
28
  app.mount('#app');
@@ -3,9 +3,12 @@ import tailwindcss from '@tailwindcss/vite';
3
3
  import vue from '@vitejs/plugin-vue';
4
4
  import { defineConfig, loadEnv } from 'vite';
5
5
 
6
+ const DEFAULT_PORT = {{PORT}};
7
+
6
8
  export default defineConfig(({ mode }) => {
7
9
  const env = loadEnv(mode, __dirname, '');
8
10
  const appName = env.VITE_APP_NAME?.trim();
11
+ const port = Number(env.LOCALHOST_PORT) || DEFAULT_PORT;
9
12
 
10
13
  if (!appName) {
11
14
  throw new Error('Missing VITE_APP_NAME for Vite config');
@@ -15,7 +18,7 @@ export default defineConfig(({ mode }) => {
15
18
  plugins: [
16
19
  bardiocDevAuth({ appName }),
17
20
  vue(),
18
- bardiocApp({ appName, port: {{PORT}} }),
21
+ bardiocApp({ appName, port }),
19
22
  tailwindcss(),
20
23
  ],
21
24
  };
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { startDevAuthProxy } from '@bardioc/app-sdk/dev-proxy';
4
- import { dirname, resolve } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
-
7
- const projectDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
8
-
9
- // Angular's dev server cannot host the SDK Vite plugin, so the proxy runs beside it
10
- // on the app port + 100 — the same origin src/main.ts points installDevBridge at.
11
- const server = await startDevAuthProxy({ projectDir, port: {{PORT}} + 100 });
12
-
13
- // Without DEV_SECRET / API_BASE_URL there is nothing to serve — exit and let the dev server run
14
- // on its own, matching how the SDK Vite plugin disables itself.
15
- if (!server) {
16
- process.exit(0);
17
- }
@@ -1,5 +0,0 @@
1
- import { createHostBridge, type HostBridge } from '@bardioc/app-sdk';
2
-
3
- export function createBardiocBridge(): HostBridge {
4
- return createHostBridge({});
5
- }
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { startDevAuthProxy } from '@bardioc/app-sdk/dev-proxy';
4
- import { dirname, resolve } from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
-
7
- const projectDir = resolve(dirname(fileURLToPath(import.meta.url)), '..');
8
-
9
- // `next dev` cannot host the SDK Vite plugin, so the proxy runs beside it on the app
10
- // port + 100 — the same origin app/page.tsx points installDevBridge at.
11
- const server = await startDevAuthProxy({ projectDir, port: {{PORT}} + 100 });
12
-
13
- // Without DEV_SECRET / API_BASE_URL there is nothing to serve — exit and let the dev server run
14
- // on its own, matching how the SDK Vite plugin disables itself.
15
- if (!server) {
16
- process.exit(0);
17
- }