@revenexx/studio-shared 0.1.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.
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ active?: boolean
4
+ icon?: unknown
5
+ label: string
6
+ }>()
7
+ </script>
8
+
9
+ <template>
10
+ <button
11
+ type="button"
12
+ class="group flex items-center gap-1.5 rounded-md px-2 py-1 text-sm font-medium transition-colors"
13
+ :class="active
14
+ ? 'bg-[#f3f1fa] text-[#0B0522]'
15
+ : 'text-foreground/70 hover:bg-muted/50 hover:text-foreground'"
16
+ >
17
+ <component
18
+ :is="icon"
19
+ v-if="icon"
20
+ :size="12"
21
+ :stroke-width="2"
22
+ class="shrink-0"
23
+ />
24
+ <span class="whitespace-nowrap">{{ label }}</span>
25
+ </button>
26
+ </template>
@@ -0,0 +1,80 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * Multi-step wizard shell (PE-114). Renders a Stepper header and a slot
4
+ * for the active step's content, with Back / Next / Finish controls.
5
+ * Steps are declarative; the parent owns each step's fields and can
6
+ * gate advancement with `canAdvance`. A reusable replacement for the
7
+ * inline-cards pattern used by the create flows.
8
+ *
9
+ * Usage:
10
+ * <Wizard :steps="steps" v-model="step" :can-advance="valid" @finish="submit">
11
+ * <template #default="{ step }">…fields for `step.key`…</template>
12
+ * </Wizard>
13
+ */
14
+ import { Button } from '@revenexx/studio'
15
+
16
+ interface Step { key: string, title: string, description?: string }
17
+
18
+ const props = withDefaults(defineProps<{
19
+ steps: Step[]
20
+ modelValue?: number
21
+ /** Gate the Next/Finish button for the current step. */
22
+ canAdvance?: boolean
23
+ busy?: boolean
24
+ }>(), { modelValue: 0, canAdvance: true, busy: false })
25
+
26
+ const emit = defineEmits<{ 'update:modelValue': [number], 'finish': [] }>()
27
+
28
+ const index = computed({
29
+ get: () => props.modelValue,
30
+ set: v => emit('update:modelValue', v),
31
+ })
32
+
33
+ const isFirst = computed(() => index.value === 0)
34
+ const isLast = computed(() => index.value === props.steps.length - 1)
35
+ const activeStep = computed(() => props.steps[index.value]!)
36
+
37
+ function back() { if (!isFirst.value) index.value-- }
38
+ function next() {
39
+ if (!props.canAdvance || props.busy) return
40
+ if (isLast.value) emit('finish')
41
+ else index.value++
42
+ }
43
+ // Allow jumping back to a completed step via the stepper header.
44
+ function goTo(i: number) { if (i < index.value) index.value = i }
45
+ </script>
46
+
47
+ <template>
48
+ <div class="space-y-5">
49
+ <!-- Go-Live-style stepper: a fill bar + label per step, completed steps
50
+ are clickable to jump back. -->
51
+ <div class="flex gap-3">
52
+ <div
53
+ v-for="(s, i) in steps"
54
+ :key="s.key"
55
+ class="flex-1"
56
+ :class="i < index ? 'cursor-pointer' : ''"
57
+ @click="goTo(i)"
58
+ >
59
+ <div class="h-1 rounded-full transition-colors" :class="i <= index ? 'bg-primary' : 'bg-border'" />
60
+ <div class="mt-2 text-xs font-medium" :class="i <= index ? 'text-primary' : 'text-muted-foreground'">Step {{ i + 1 }}</div>
61
+ <div class="text-sm" :class="i <= index ? 'text-[#0B0522]' : 'text-muted-foreground'">{{ s.title }}</div>
62
+ </div>
63
+ </div>
64
+
65
+ <div>
66
+ <slot :step="activeStep" :index="index" />
67
+ </div>
68
+
69
+ <div class="flex items-center justify-between border-t pt-3">
70
+ <!-- Custom footer (e.g. multiple finish actions on a review step). Falls
71
+ back to the standard Back / Next·Finish pair. -->
72
+ <slot name="footer" :back="back" :next="next" :is-first="isFirst" :is-last="isLast" :can-advance="canAdvance" :busy="busy" :index="index">
73
+ <Button variant="ghost" :disabled="isFirst || busy" @click="back">Back</Button>
74
+ <Button :disabled="!canAdvance || busy" class="gap-1" @click="next">
75
+ {{ isLast ? 'Finish' : 'Next' }}
76
+ </Button>
77
+ </slot>
78
+ </div>
79
+ </div>
80
+ </template>
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Platform auth provider — the seam that lets a studio surface run either
3
+ * inside the full cockpit or standalone.
4
+ *
5
+ * This is the **standalone default**: it sources a bearer token from runtime
6
+ * config (`NUXT_PUBLIC_DEV_TOKEN`) or `localStorage['console.token']`. The
7
+ * full cockpit ships an override at `app/composables/usePlatformAuth.ts`
8
+ * (app-level auto-imports take precedence over this layer) that returns the
9
+ * real Appwrite/Console-sourced operator token.
10
+ *
11
+ * Studio modules (e.g. `@revenexx/studio-integrations`) call this instead of
12
+ * `useConsoleAuth` directly, so they carry no cockpit-only dependency.
13
+ */
14
+ export interface PlatformAuth {
15
+ getToken: () => Promise<string | null>
16
+ }
17
+
18
+ export function usePlatformAuth(): PlatformAuth {
19
+ const config = useRuntimeConfig()
20
+ return {
21
+ async getToken() {
22
+ const fromConfig = (config.public.devToken as string | undefined) || null
23
+ if (fromConfig) return fromConfig
24
+ if (import.meta.client) {
25
+ try {
26
+ return localStorage.getItem('console.token')
27
+ }
28
+ catch {
29
+ return null
30
+ }
31
+ }
32
+ return null
33
+ },
34
+ }
35
+ }
@@ -0,0 +1,35 @@
1
+ import { computed, ref } from 'vue'
2
+ import type { Ref } from 'vue'
3
+
4
+ /**
5
+ * Platform tenant provider — companion to `usePlatformAuth`.
6
+ *
7
+ * Standalone default: the active tenant slug comes from runtime config
8
+ * (`NUXT_PUBLIC_DEV_TENANT`) or `localStorage['cockpit.devTenant']`. The full
9
+ * cockpit overrides this at `app/composables/usePlatformTenant.ts` to return
10
+ * the real active-tenant slug from `useConsoleTenants`.
11
+ */
12
+ export interface PlatformTenant {
13
+ slug: Ref<string | null>
14
+ load: () => Promise<void>
15
+ }
16
+
17
+ export function usePlatformTenant(): PlatformTenant {
18
+ const config = useRuntimeConfig()
19
+ const stored = ref<string | null>(null)
20
+ if (import.meta.client) {
21
+ try {
22
+ stored.value = localStorage.getItem('cockpit.devTenant')
23
+ }
24
+ catch {
25
+ stored.value = null
26
+ }
27
+ }
28
+ const slug = computed<string | null>(() => (config.public.devTenant as string | undefined) || stored.value)
29
+ return {
30
+ slug: slug as unknown as Ref<string | null>,
31
+ async load() {
32
+ // no-op in standalone; the slug is static from config/localStorage.
33
+ },
34
+ }
35
+ }
package/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Type-level barrel for `@revenexx/studio-shared`.
3
+ *
4
+ * The shell primitive *components* auto-import by bare name via the Nuxt
5
+ * layer (see `nuxt.config.ts`). This barrel exposes the *types* consumers
6
+ * need to import explicitly — DataTable column/filter/sort types and the
7
+ * SidebarNav section/item shapes.
8
+ */
9
+ export type * from './components/DataTable/types'
10
+ export type { SidebarNavItem, SidebarNavSection } from './components/Shared/SidebarNav.vue'
11
+ export type { PlatformAuth } from './composables/usePlatformAuth'
12
+ export type { PlatformTenant } from './composables/usePlatformTenant'
package/nuxt.config.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { fileURLToPath } from 'node:url'
2
+ import { dirname, join } from 'node:path'
3
+
4
+ const currentDir = dirname(fileURLToPath(import.meta.url))
5
+
6
+ /**
7
+ * Shared cockpit shell primitives, provided as a Nuxt layer.
8
+ *
9
+ * Components are auto-imported by their **bare** name (`pathPrefix: false`),
10
+ * so `<PageHeader>`, `<DataTable>`, `<SidebarNav>`, `<StateCard>`, `<TabBar>`,
11
+ * … resolve without an explicit import — matching how the cockpit already
12
+ * uses them. Type-level exports (DataTable column/filter types,
13
+ * SidebarNavSection) come from the package barrel `@revenexx/studio-shared`.
14
+ */
15
+ export default defineNuxtConfig({
16
+ $meta: {
17
+ name: 'studio-shared',
18
+ description: 'Shared cockpit shell primitives + design-system wiring.',
19
+ },
20
+ components: [
21
+ { path: join(currentDir, 'components'), pathPrefix: false },
22
+ ],
23
+ })
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@revenexx/studio-shared",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Shared revenexx Studio shell primitives (PageHeader, SidebarNav, StateCard, DataTable, …) + the platform provider contract (usePlatformAuth/usePlatformTenant). A Nuxt layer — extend it, then add a studio module.",
6
+ "main": "./nuxt.config.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./index.ts",
10
+ "import": "./index.ts"
11
+ },
12
+ "./nuxt.config": "./nuxt.config.ts"
13
+ },
14
+ "files": [
15
+ "nuxt.config.ts",
16
+ "index.ts",
17
+ "components",
18
+ "composables"
19
+ ],
20
+ "publishConfig": {
21
+ "registry": "https://registry.npmjs.org/",
22
+ "access": "public"
23
+ },
24
+ "peerDependencies": {
25
+ "@revenexx/studio": "*",
26
+ "nuxt": "^4.0.0",
27
+ "vue": "^3.5.0"
28
+ }
29
+ }