specra 0.1.0 → 0.1.1
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/dist/app/docs-page.js +164 -148
- package/dist/app/docs-page.js.map +1 -1
- package/dist/app/docs-page.mjs +3 -3
- package/dist/app/layout.js +84 -5
- package/dist/app/layout.js.map +1 -1
- package/dist/app/layout.mjs +3 -3
- package/dist/{chunk-NXRIAL7T.mjs → chunk-4QHOMV5A.mjs} +8 -5
- package/dist/chunk-4QHOMV5A.mjs.map +1 -0
- package/dist/{chunk-IZFGEAD6.mjs → chunk-5AYOV2KD.mjs} +5 -4
- package/dist/chunk-5AYOV2KD.mjs.map +1 -0
- package/dist/chunk-I3ELVEK2.mjs +56 -0
- package/dist/chunk-I3ELVEK2.mjs.map +1 -0
- package/dist/{chunk-INL2EC72.mjs → chunk-RLMSINLY.mjs} +2 -5
- package/dist/{chunk-INL2EC72.mjs.map → chunk-RLMSINLY.mjs.map} +1 -1
- package/dist/components/index.d.mts +39 -3
- package/dist/components/index.d.ts +39 -3
- package/dist/components/index.js +100 -62
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +83 -48
- package/dist/components/index.mjs.map +1 -1
- package/dist/index.d.mts +39 -3
- package/dist/index.d.ts +39 -3
- package/dist/index.js +129 -94
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -4
- package/dist/index.mjs.map +1 -1
- package/dist/lib/index.d.mts +7 -0
- package/dist/lib/index.d.ts +7 -0
- package/dist/lib/index.js +1 -4
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +1 -1
- package/package.json +2 -2
- package/src/app/layout.tsx +6 -3
- package/src/components/docs/header.tsx +7 -3
- package/src/components/docs/index.ts +3 -0
- package/src/lib/config.context.tsx +65 -0
- package/src/lib/config.server.ts +9 -5
- package/src/lib/index.ts +1 -0
- package/dist/chunk-IZFGEAD6.mjs.map +0 -1
- package/dist/chunk-MZJHJ6BV.mjs +0 -21
- package/dist/chunk-MZJHJ6BV.mjs.map +0 -1
- package/dist/chunk-NXRIAL7T.mjs.map +0 -1
package/dist/lib/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specra",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A modern documentation library for Next.js with built-in versioning, API reference generation, full-text search, and MDX support",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"license": "MIT",
|
|
57
57
|
"repository": {
|
|
58
58
|
"type": "git",
|
|
59
|
-
"url": "https://github.com/
|
|
59
|
+
"url": "https://github.com/dalmasonto/specra"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|
package/src/app/layout.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import type { Metadata } from "next"
|
|
|
3
3
|
import { Geist, Geist_Mono } from "next/font/google"
|
|
4
4
|
import { getConfig } from "../lib/config"
|
|
5
5
|
import { getAssetPath } from "../lib/utils"
|
|
6
|
+
import { ConfigProvider } from "../lib/config.context"
|
|
6
7
|
import { TabProvider } from "../components/docs/tab-context"
|
|
7
8
|
import "../styles/globals.css"
|
|
8
9
|
|
|
@@ -65,9 +66,11 @@ export default function RootLayout({
|
|
|
65
66
|
return (
|
|
66
67
|
<html lang={config.site.language || "en"} suppressHydrationWarning>
|
|
67
68
|
<body className={`${geist.className} font-sans antialiased`}>
|
|
68
|
-
<
|
|
69
|
-
{
|
|
70
|
-
|
|
69
|
+
<ConfigProvider config={config}>
|
|
70
|
+
<TabProvider defaultTab={defaultTab}>
|
|
71
|
+
{children}
|
|
72
|
+
</TabProvider>
|
|
73
|
+
</ConfigProvider>
|
|
71
74
|
</body>
|
|
72
75
|
</html>
|
|
73
76
|
)
|
|
@@ -8,16 +8,20 @@ import { ThemeToggle } from "./theme-toggle"
|
|
|
8
8
|
import { SearchModal } from "./search-modal"
|
|
9
9
|
import { useState, useEffect } from "react"
|
|
10
10
|
import type { SpecraConfig } from "@/lib/config"
|
|
11
|
+
import { useConfig } from "@/lib/config.context"
|
|
11
12
|
import { getAssetPath } from "@/lib/utils"
|
|
12
13
|
|
|
13
14
|
interface HeaderProps {
|
|
14
15
|
currentVersion: string
|
|
15
16
|
versions: string[]
|
|
16
17
|
onMenuClick?: () => void
|
|
17
|
-
config
|
|
18
|
+
config?: SpecraConfig // Made optional since we can get it from context
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
export function Header({ currentVersion, versions, onMenuClick, config }: HeaderProps) {
|
|
21
|
+
export function Header({ currentVersion, versions, onMenuClick, config: configProp }: HeaderProps) {
|
|
22
|
+
// Use config from context if not provided as prop
|
|
23
|
+
const contextConfig = useConfig()
|
|
24
|
+
const config = configProp || contextConfig
|
|
21
25
|
const [searchOpen, setSearchOpen] = useState(false)
|
|
22
26
|
|
|
23
27
|
// Keyboard shortcut for search (Cmd+K or Ctrl+K)
|
|
@@ -54,7 +58,7 @@ export function Header({ currentVersion, versions, onMenuClick, config }: Header
|
|
|
54
58
|
</span>
|
|
55
59
|
</div>
|
|
56
60
|
)}
|
|
57
|
-
<span className="font-semibold text-lg text-foreground">Specra</span>
|
|
61
|
+
<span className="font-semibold text-lg text-foreground">{config.site.title ?? "Specra"}</span>
|
|
58
62
|
</Link>
|
|
59
63
|
</div>
|
|
60
64
|
|
|
@@ -44,5 +44,8 @@ export * from "./tooltip"
|
|
|
44
44
|
export * from "./version-switcher"
|
|
45
45
|
export * from "./video"
|
|
46
46
|
|
|
47
|
+
// Re-export ConfigProvider from lib (client component for config context)
|
|
48
|
+
export { ConfigProvider, useConfig, useConfigValue } from "../../lib/config.context"
|
|
49
|
+
|
|
47
50
|
// API documentation components
|
|
48
51
|
export * from "./api"
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { SpecraConfig, defaultConfig } from "./config.types"
|
|
3
|
+
|
|
4
|
+
const ConfigContext = React.createContext<SpecraConfig>(defaultConfig)
|
|
5
|
+
|
|
6
|
+
export interface ConfigProviderProps {
|
|
7
|
+
config: SpecraConfig
|
|
8
|
+
children: React.ReactNode
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Provider component that makes Specra config available to all client components
|
|
13
|
+
* Usage: Wrap your app with this provider in your root layout
|
|
14
|
+
*/
|
|
15
|
+
export function ConfigProvider({ config, children }: ConfigProviderProps) {
|
|
16
|
+
return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hook to access Specra configuration in any client component
|
|
21
|
+
* @returns The current Specra configuration
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* function MyComponent() {
|
|
25
|
+
* const config = useConfig()
|
|
26
|
+
* return <div>{config.site.title}</div>
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function useConfig(): SpecraConfig {
|
|
31
|
+
const config = React.useContext(ConfigContext)
|
|
32
|
+
if (!config) {
|
|
33
|
+
throw new Error("useConfig must be used within a ConfigProvider")
|
|
34
|
+
}
|
|
35
|
+
return config
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Hook to access a specific configuration value by path
|
|
40
|
+
* @param path - Dot-separated path to the config value (e.g., "site.title")
|
|
41
|
+
* @returns The configuration value at the specified path
|
|
42
|
+
* @example
|
|
43
|
+
* ```tsx
|
|
44
|
+
* function MyComponent() {
|
|
45
|
+
* const title = useConfigValue<string>("site.title")
|
|
46
|
+
* const showSidebar = useConfigValue<boolean>("navigation.showSidebar")
|
|
47
|
+
* return <div>{title}</div>
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function useConfigValue<T = any>(path: string): T | undefined {
|
|
52
|
+
const config = useConfig()
|
|
53
|
+
const keys = path.split(".")
|
|
54
|
+
let value: any = config
|
|
55
|
+
|
|
56
|
+
for (const key of keys) {
|
|
57
|
+
if (value && typeof value === "object" && key in value) {
|
|
58
|
+
value = value[key]
|
|
59
|
+
} else {
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return value as T
|
|
65
|
+
}
|
package/src/lib/config.server.ts
CHANGED
|
@@ -137,21 +137,25 @@ export function validateConfig(config: SpecraConfig): { valid: boolean; errors:
|
|
|
137
137
|
// Singleton instance
|
|
138
138
|
let configInstance: SpecraConfig | null = null
|
|
139
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Initialize the Specra configuration
|
|
142
|
+
* Can be called multiple times - subsequent calls will update the config
|
|
143
|
+
* @param userConfig - Partial configuration to merge with defaults
|
|
144
|
+
* @returns The initialized configuration
|
|
145
|
+
*/
|
|
140
146
|
export function initConfig(userConfig: Partial<SpecraConfig>): SpecraConfig {
|
|
141
|
-
if (configInstance) {
|
|
142
|
-
throw new Error("Specra config has already been initialized")
|
|
143
|
-
}
|
|
144
|
-
|
|
145
147
|
configInstance = loadConfig(userConfig)
|
|
146
148
|
return configInstance
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
/**
|
|
150
152
|
* Get the configuration instance (cached) (SERVER ONLY)
|
|
153
|
+
* If not initialized, returns default config
|
|
151
154
|
*/
|
|
152
155
|
export function getConfig(): SpecraConfig {
|
|
153
156
|
if (!configInstance) {
|
|
154
|
-
|
|
157
|
+
// Auto-initialize with defaults if not already initialized
|
|
158
|
+
configInstance = loadConfig({})
|
|
155
159
|
}
|
|
156
160
|
return configInstance
|
|
157
161
|
}
|
package/src/lib/index.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/app/layout.tsx"],"sourcesContent":["import type React from \"react\"\nimport type { Metadata } from \"next\"\nimport { Geist, Geist_Mono } from \"next/font/google\"\nimport { getConfig } from \"../lib/config\"\nimport { getAssetPath } from \"../lib/utils\"\nimport { TabProvider } from \"../components/docs/tab-context\"\nimport \"../styles/globals.css\"\n\nconst geist = Geist({ subsets: [\"latin\"] })\nconst geistMono = Geist_Mono({ subsets: [\"latin\"] })\n\n/**\n * Generate metadata for the root layout\n * This can be imported and used by the user's app/layout.tsx\n */\nexport function generateMetadata(): Metadata {\n const config = getConfig()\n\n return {\n title: {\n default: config.site.title,\n template: `%s | ${config.site.title}`,\n },\n description: config.site.description || \"Modern documentation platform\",\n generator: \"Specra Documentation\",\n metadataBase: config.site.url ? new URL(config.site.url) : undefined,\n icons: {\n icon: getAssetPath(config.site.favicon ?? \"\") ? [\n {\n url: getAssetPath(config.site.favicon ?? \"\"),\n },\n ] : [],\n apple: getAssetPath(\"/apple-icon.png\"),\n },\n openGraph: {\n title: config.site.title,\n description: config.site.description,\n url: config.site.url,\n siteName: config.site.title,\n locale: config.site.language || \"en\",\n type: \"website\",\n },\n twitter: {\n card: \"summary_large_image\",\n title: config.site.title,\n description: config.site.description,\n },\n }\n}\n\nexport const metadata: Metadata = generateMetadata()\n\n/**\n * Root layout component for Specra documentation sites\n * This provides the HTML structure and global providers\n */\nexport default function RootLayout({\n children,\n}: Readonly<{\n children: React.ReactNode\n}>) {\n const config = getConfig()\n const defaultTab = config.navigation?.tabGroups?.[0]?.id || \"\"\n\n return (\n <html lang={config.site.language || \"en\"} suppressHydrationWarning>\n <body className={`${geist.className} font-sans antialiased`}>\n <TabProvider defaultTab={defaultTab}>\n {children}\n </TabProvider>\n </body>\n </html>\n )\n}"],"mappings":";;;;;;;;;AAEA,SAAS,OAAO,kBAAkB;AAiE1B;AA3DR,IAAM,QAAQ,MAAM,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;AAC1C,IAAM,YAAY,WAAW,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC;AAM5C,SAAS,mBAA6B;AAC3C,QAAM,SAAS,UAAU;AAEzB,SAAO;AAAA,IACL,OAAO;AAAA,MACL,SAAS,OAAO,KAAK;AAAA,MACrB,UAAU,QAAQ,OAAO,KAAK,KAAK;AAAA,IACrC;AAAA,IACA,aAAa,OAAO,KAAK,eAAe;AAAA,IACxC,WAAW;AAAA,IACX,cAAc,OAAO,KAAK,MAAM,IAAI,IAAI,OAAO,KAAK,GAAG,IAAI;AAAA,IAC3D,OAAO;AAAA,MACL,MAAM,aAAa,OAAO,KAAK,WAAW,EAAE,IAAI;AAAA,QAC9C;AAAA,UACE,KAAK,aAAa,OAAO,KAAK,WAAW,EAAE;AAAA,QAC7C;AAAA,MACF,IAAI,CAAC;AAAA,MACL,OAAO,aAAa,iBAAiB;AAAA,IACvC;AAAA,IACA,WAAW;AAAA,MACT,OAAO,OAAO,KAAK;AAAA,MACnB,aAAa,OAAO,KAAK;AAAA,MACzB,KAAK,OAAO,KAAK;AAAA,MACjB,UAAU,OAAO,KAAK;AAAA,MACtB,QAAQ,OAAO,KAAK,YAAY;AAAA,MAChC,MAAM;AAAA,IACR;AAAA,IACA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,OAAO,OAAO,KAAK;AAAA,MACnB,aAAa,OAAO,KAAK;AAAA,IAC3B;AAAA,EACF;AACF;AAEO,IAAM,WAAqB,iBAAiB;AAMpC,SAAR,WAA4B;AAAA,EACjC;AACF,GAEI;AACF,QAAM,SAAS,UAAU;AACzB,QAAM,aAAa,OAAO,YAAY,YAAY,CAAC,GAAG,MAAM;AAE5D,SACE,oBAAC,UAAK,MAAM,OAAO,KAAK,YAAY,MAAM,0BAAwB,MAChE,8BAAC,UAAK,WAAW,GAAG,MAAM,SAAS,0BACjC,8BAAC,eAAY,YACV,UACH,GACF,GACF;AAEJ;","names":[]}
|
package/dist/chunk-MZJHJ6BV.mjs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// src/components/docs/tab-context.tsx
|
|
2
|
-
import { createContext, useContext, useState } from "react";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
var TabContext = createContext(void 0);
|
|
5
|
-
function TabProvider({ children, defaultTab }) {
|
|
6
|
-
const [activeTabGroup, setActiveTabGroup] = useState(defaultTab);
|
|
7
|
-
return /* @__PURE__ */ jsx(TabContext.Provider, { value: { activeTabGroup, setActiveTabGroup }, children });
|
|
8
|
-
}
|
|
9
|
-
function useTabContext() {
|
|
10
|
-
const context = useContext(TabContext);
|
|
11
|
-
if (!context) {
|
|
12
|
-
throw new Error("useTabContext must be used within TabProvider");
|
|
13
|
-
}
|
|
14
|
-
return context;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export {
|
|
18
|
-
TabProvider,
|
|
19
|
-
useTabContext
|
|
20
|
-
};
|
|
21
|
-
//# sourceMappingURL=chunk-MZJHJ6BV.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/docs/tab-context.tsx"],"sourcesContent":["\"use client\"\n\nimport { createContext, useContext, useState, ReactNode } from \"react\"\n\ninterface TabContextType {\n activeTabGroup: string\n setActiveTabGroup: (tabId: string) => void\n}\n\nconst TabContext = createContext<TabContextType | undefined>(undefined)\n\nexport function TabProvider({ children, defaultTab }: { children: ReactNode; defaultTab: string }) {\n const [activeTabGroup, setActiveTabGroup] = useState(defaultTab)\n\n return (\n <TabContext.Provider value={{ activeTabGroup, setActiveTabGroup }}>\n {children}\n </TabContext.Provider>\n )\n}\n\nexport function useTabContext() {\n const context = useContext(TabContext)\n if (!context) {\n throw new Error(\"useTabContext must be used within TabProvider\")\n }\n return context\n}\n"],"mappings":";AAEA,SAAS,eAAe,YAAY,gBAA2B;AAa3D;AANJ,IAAM,aAAa,cAA0C,MAAS;AAE/D,SAAS,YAAY,EAAE,UAAU,WAAW,GAAgD;AACjG,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAS,UAAU;AAE/D,SACE,oBAAC,WAAW,UAAX,EAAoB,OAAO,EAAE,gBAAgB,kBAAkB,GAC7D,UACH;AAEJ;AAEO,SAAS,gBAAgB;AAC9B,QAAM,UAAU,WAAW,UAAU;AACrC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AACA,SAAO;AACT;","names":[]}
|