@kawaiininja/layouts 1.0.4 → 1.0.6
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/README.md +79 -70
- package/dist/MobileLayout.js +37 -14
- package/dist/types.d.ts +5 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @kawaiininja/layouts
|
|
2
2
|
|
|
3
3
|
High-performance, premium mobile-first layouts for the Onyx Framework. Designed with a focus on fluid gestures, modularity, and state-of-the-art aesthetics.
|
|
4
4
|
|
|
@@ -26,6 +26,12 @@ High-performance, premium mobile-first layouts for the Onyx Framework. Designed
|
|
|
26
26
|
- **Flexible Side Drawer**: Fully configurable drawer menu with support for deep-linking (target specific tabs/sub-tabs) and custom action links.
|
|
27
27
|
- **Panel System**: Register any number of custom drawers (Bottom Sheets) that can be triggered from anywhere in the app.
|
|
28
28
|
|
|
29
|
+
### 🛣️ Integrated Routing Support
|
|
30
|
+
|
|
31
|
+
- **Full URL Sync**: Sync layout state with `react-router-dom` or any history-based router.
|
|
32
|
+
- **Controlled Navigation**: Pass `activeTab` and `activeSubTab` from your routing hook to drive the UI.
|
|
33
|
+
- **Path Mapping**: Define `path` strings for tabs and sub-tabs to automatically trigger the `onNavigate` callback.
|
|
34
|
+
|
|
29
35
|
### 🎨 Design System & Theme
|
|
30
36
|
|
|
31
37
|
- **Premium Glassmorphism**: High-quality backdrop blurs and subtle gradients for a modern, high-end feel.
|
|
@@ -36,7 +42,7 @@ High-performance, premium mobile-first layouts for the Onyx Framework. Designed
|
|
|
36
42
|
## 🚀 Installation
|
|
37
43
|
|
|
38
44
|
```bash
|
|
39
|
-
npm install @
|
|
45
|
+
npm install @kawaiininja/layouts
|
|
40
46
|
```
|
|
41
47
|
|
|
42
48
|
Note: This package requires `framer-motion`, `lucide-react`, and `react` >= 18.0.0.
|
|
@@ -45,42 +51,53 @@ Note: This package requires `framer-motion`, `lucide-react`, and `react` >= 18.0
|
|
|
45
51
|
|
|
46
52
|
## 📦 Usage
|
|
47
53
|
|
|
48
|
-
###
|
|
54
|
+
### Integrated with React Router
|
|
49
55
|
|
|
50
56
|
```jsx
|
|
51
|
-
import { OnyxMobileLayout } from "@
|
|
52
|
-
import {
|
|
57
|
+
import { OnyxMobileLayout } from "@kawaiininja/layouts";
|
|
58
|
+
import { BrowserRouter, useNavigate, useLocation } from "react-router-dom";
|
|
59
|
+
import { Home, Grid, PenTool } from "lucide-react";
|
|
60
|
+
|
|
61
|
+
const App = () => {
|
|
62
|
+
const navigate = useNavigate();
|
|
63
|
+
const location = useLocation();
|
|
64
|
+
const currentPath = location.pathname;
|
|
53
65
|
|
|
54
|
-
const MyApp = () => {
|
|
55
66
|
const tabs = [
|
|
56
67
|
{
|
|
57
68
|
id: "home",
|
|
58
69
|
label: "Home",
|
|
59
70
|
icon: Home,
|
|
60
|
-
|
|
71
|
+
path: "/home",
|
|
61
72
|
subTabs: [
|
|
62
|
-
{
|
|
73
|
+
{
|
|
74
|
+
label: "Feed",
|
|
75
|
+
icon: Grid,
|
|
76
|
+
path: "feed",
|
|
77
|
+
view: () => <div>Main Feed</div>,
|
|
78
|
+
},
|
|
63
79
|
],
|
|
64
80
|
quickActions: [
|
|
65
81
|
{
|
|
66
82
|
label: "New Post",
|
|
67
83
|
icon: PenTool,
|
|
68
|
-
onClick: ({ openDrawer }) => openDrawer("
|
|
84
|
+
onClick: ({ openDrawer }) => openDrawer("editor"),
|
|
69
85
|
},
|
|
70
86
|
],
|
|
71
87
|
},
|
|
72
88
|
];
|
|
73
89
|
|
|
90
|
+
// Derive active states from URL
|
|
91
|
+
const activeTabId = tabs.find((t) => currentPath.startsWith(t.path))?.id;
|
|
92
|
+
|
|
74
93
|
return (
|
|
75
94
|
<OnyxMobileLayout
|
|
76
95
|
tabs={tabs}
|
|
77
|
-
user={{
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
avatar: "https://example.com/avatar.png",
|
|
81
|
-
}}
|
|
96
|
+
user={{ name: "Alex", handle: "@alex", avatar: "..." }}
|
|
97
|
+
activeTab={activeTabId}
|
|
98
|
+
onNavigate={(path) => navigate(path)}
|
|
82
99
|
drawers={{
|
|
83
|
-
|
|
100
|
+
editor: MyEditorPanel,
|
|
84
101
|
}}
|
|
85
102
|
/>
|
|
86
103
|
);
|
|
@@ -93,51 +110,54 @@ const MyApp = () => {
|
|
|
93
110
|
|
|
94
111
|
### `OnyxMobileLayoutProps`
|
|
95
112
|
|
|
96
|
-
| Prop | Type | Description
|
|
97
|
-
| :------------- | :-------------------------- |
|
|
98
|
-
| `tabs` | `TabConfig[]` | **Required**. The main navigation stack (Rails on the right).
|
|
99
|
-
| `user` | `UserConfig` | **Required**. Data for the side drawer's profile section.
|
|
100
|
-
| `drawers` | `Record<string, Component>` | Dictionary of custom panel components.
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
113
|
+
| Prop | Type | Description |
|
|
114
|
+
| :------------- | :-------------------------- | :------------------------------------------------------------ |
|
|
115
|
+
| `tabs` | `TabConfig[]` | **Required**. The main navigation stack (Rails on the right). |
|
|
116
|
+
| `user` | `UserConfig` | **Required**. Data for the side drawer's profile section. |
|
|
117
|
+
| `drawers` | `Record<string, Component>` | Dictionary of custom panel components. |
|
|
118
|
+
| `activeTab` | `string` | Controlled ID of the active tab. |
|
|
119
|
+
| `activeSubTab` | `string` | Controlled label of the active sub-tab. |
|
|
120
|
+
| `onNavigate` | `(path: string) => void` | Triggered on path-based navigation (Rails, Tabs, Drawers). |
|
|
121
|
+
| `drawerItems` | `DrawerItemConfig[]` | Custom links for the side menu. |
|
|
122
|
+
| `onSignOut` | `() => void` | Triggered when the "Sign Out" button is clicked. |
|
|
123
|
+
| `onRefresh` | `() => void` | Global pull-to-refresh handler. |
|
|
124
|
+
| `isRefreshing` | `boolean` | Global refresh state. |
|
|
125
|
+
| `initialTab` | `string` | Default: `home`. |
|
|
126
|
+
| `rightAction` | `ReactNode` | Global header action element. |
|
|
107
127
|
|
|
108
128
|
### `TabConfig`
|
|
109
129
|
|
|
110
|
-
| Field | Type | Description
|
|
111
|
-
| :------------- | :-------------------- |
|
|
112
|
-
| `id` | `string` | Unique identifier
|
|
113
|
-
| `label` | `string` |
|
|
114
|
-
| `icon` | `LucideIcon` |
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
130
|
+
| Field | Type | Description |
|
|
131
|
+
| :------------- | :-------------------- | :------------------------------ |
|
|
132
|
+
| `id` | `string` | Unique identifier. |
|
|
133
|
+
| `label` | `string` | Rail label. |
|
|
134
|
+
| `icon` | `LucideIcon` | Rail icon. |
|
|
135
|
+
| `path` | `string` | Absolute base path for routing. |
|
|
136
|
+
| `navTitle` | `string` | Optional header title. |
|
|
137
|
+
| `subTabs` | `SubTabConfig[]` | Nested horizontal navigation. |
|
|
138
|
+
| `quickActions` | `QuickActionConfig[]` | Radial menu items. |
|
|
139
|
+
| `onRefresh` | `() => void` | Tab-specific refresh handler. |
|
|
140
|
+
| `isRefreshing` | `boolean` | Tab-specific loading state. |
|
|
141
|
+
| `rightAction` | `ReactNode` | Tab-specific header action. |
|
|
121
142
|
|
|
122
143
|
### `SubTabConfig`
|
|
123
144
|
|
|
124
|
-
| Field | Type | Description
|
|
125
|
-
| :------ | :----------- |
|
|
126
|
-
| `label` | `string` |
|
|
127
|
-
| `icon` | `LucideIcon` |
|
|
128
|
-
| `
|
|
145
|
+
| Field | Type | Description |
|
|
146
|
+
| :------ | :----------- | :------------------------------------- |
|
|
147
|
+
| `label` | `string` | Pill label. |
|
|
148
|
+
| `icon` | `LucideIcon` | Pill icon. |
|
|
149
|
+
| `path` | `string` | Relative or absolute path for routing. |
|
|
150
|
+
| `view` | `Component` | The component to render. |
|
|
129
151
|
|
|
130
152
|
### `QuickActionConfig` & `DrawerItemConfig`
|
|
131
153
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
|
135
|
-
|
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `targetSubTab` | `string` | (Drawer only) Target Sub-tab Label for deep-linking. |
|
|
140
|
-
| `onClick` | `(ctx) => void` | Custom callback. Context contains `{ openDrawer: (id: string) => void }`. |
|
|
154
|
+
| Prop | Type | Description |
|
|
155
|
+
| :------------- | :-------------- | :----------------------------------------------- |
|
|
156
|
+
| `label` | `string` | Item text. |
|
|
157
|
+
| `icon` | `LucideIcon` | Item icon. |
|
|
158
|
+
| `targetTab` | `string` | Target Tab ID for navigation. |
|
|
159
|
+
| `targetSubTab` | `string` | Target Sub-tab Label for deep-linking. |
|
|
160
|
+
| `onClick` | `(ctx) => void` | Context: `{ openDrawer: (id: string) => void }`. |
|
|
141
161
|
|
|
142
162
|
---
|
|
143
163
|
|
|
@@ -145,39 +165,28 @@ Both configurations share an `onClick` context provider:
|
|
|
145
165
|
|
|
146
166
|
### Pull-to-Refresh
|
|
147
167
|
|
|
148
|
-
To enable the refresh indicator, simply provide an `onRefresh` callback. The indicator will appear when the user pulls down from the top of the scroll container.
|
|
149
|
-
|
|
150
168
|
- **Threshold**: 60px pull triggers the refresh.
|
|
151
|
-
- **
|
|
169
|
+
- **Implementation**: Provide `onRefresh` and sync `isRefreshing`.
|
|
152
170
|
|
|
153
171
|
### Quick Action Menu (Radial Selection)
|
|
154
172
|
|
|
155
|
-
1. **Trigger**:
|
|
156
|
-
2. **Selection**:
|
|
157
|
-
3. **Execution**: Release
|
|
173
|
+
1. **Trigger**: Long-press (400ms) any Navigation Rail item.
|
|
174
|
+
2. **Selection**: Drag towards an action to highlight.
|
|
175
|
+
3. **Execution**: Release to trigger.
|
|
158
176
|
|
|
159
177
|
---
|
|
160
178
|
|
|
161
179
|
## 🎨 Design System (CSS Tokens)
|
|
162
180
|
|
|
163
|
-
The layout is highly skinable using CSS variables. Override these in your root CSS:
|
|
164
|
-
|
|
165
181
|
```css
|
|
166
182
|
:root {
|
|
167
|
-
|
|
168
|
-
--bg-
|
|
169
|
-
--bg-
|
|
170
|
-
--
|
|
171
|
-
|
|
172
|
-
/* Brand */
|
|
173
|
-
--color-accent: 99, 102, 241; /* Primary brand color (Indigo) */
|
|
183
|
+
--bg-main: 0, 0, 0;
|
|
184
|
+
--bg-surface: 18, 18, 18;
|
|
185
|
+
--bg-elevated: 28, 28, 28;
|
|
186
|
+
--color-accent: 99, 102, 241;
|
|
174
187
|
--color-secondary: 236, 72, 153;
|
|
175
|
-
|
|
176
|
-
/* Text */
|
|
177
188
|
--text-primary: 255, 255, 255;
|
|
178
189
|
--text-muted: 156, 163, 175;
|
|
179
|
-
|
|
180
|
-
/* Borders */
|
|
181
190
|
--color-border-subtle: 255, 255, 255, 0.1;
|
|
182
191
|
}
|
|
183
192
|
```
|
package/dist/MobileLayout.js
CHANGED
|
@@ -103,17 +103,43 @@ function useRailNavScroll(active, navKeys) {
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
// --- Main Layout Component ---
|
|
106
|
-
const OnyxMobileLayoutBase = ({ tabs, user, drawers = {}, onSignOut, onRefresh, isRefreshing: externalRefreshing, rightAction, initialTab = "home", drawerItems = [], }) => {
|
|
107
|
-
const [
|
|
106
|
+
const OnyxMobileLayoutBase = ({ tabs, user, drawers = {}, onSignOut, onRefresh, isRefreshing: externalRefreshing, rightAction, initialTab = "home", activeTab: externalActiveTab, activeSubTab: externalActiveSubTab, onNavigate, drawerItems = [], }) => {
|
|
107
|
+
const [internalActiveTab, setInternalActiveTab] = useState(initialTab);
|
|
108
|
+
const activeTab = externalActiveTab || internalActiveTab;
|
|
108
109
|
const [isOpen, setIsOpen] = useState(false);
|
|
109
110
|
const [isDragging, setIsDragging] = useState(false);
|
|
110
111
|
const [activeDrawer, setActiveDrawer] = useState(null);
|
|
111
112
|
const [pullY, setPullY] = useState(0);
|
|
112
113
|
const [internalRefreshing, setInternalRefreshing] = useState(false);
|
|
113
114
|
const currentTabConfig = tabs.find((t) => t.id === activeTab);
|
|
114
|
-
const isRefreshing = currentTabConfig?.isRefreshing ?? externalRefreshing ?? internalRefreshing;
|
|
115
115
|
const subTabs = currentTabConfig?.subTabs || [];
|
|
116
|
-
const [
|
|
116
|
+
const [internalSubTab, setInternalSubTab] = useState(subTabs[0]?.label || "");
|
|
117
|
+
const subTab = externalActiveSubTab || internalSubTab;
|
|
118
|
+
const navigateTo = (tabId, subTabLabel) => {
|
|
119
|
+
if (tabId)
|
|
120
|
+
setInternalActiveTab(tabId);
|
|
121
|
+
if (subTabLabel)
|
|
122
|
+
setInternalSubTab(subTabLabel);
|
|
123
|
+
if (onNavigate) {
|
|
124
|
+
const targetTab = tabs.find((t) => t.id === (tabId || activeTab));
|
|
125
|
+
let path = targetTab?.path || "";
|
|
126
|
+
if (subTabLabel) {
|
|
127
|
+
const st = targetTab?.subTabs.find((s) => s.label === subTabLabel);
|
|
128
|
+
if (st?.path) {
|
|
129
|
+
if (st.path.startsWith("/")) {
|
|
130
|
+
path = st.path;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const base = path.endsWith("/") ? path : path + "/";
|
|
134
|
+
path = base + st.path;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (path)
|
|
139
|
+
onNavigate(path);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const isRefreshing = currentTabConfig?.isRefreshing ?? externalRefreshing ?? internalRefreshing;
|
|
117
143
|
const navKeys = useMemo(() => tabs.map((t) => t.id), [tabs]);
|
|
118
144
|
const { scrollContainerRef, activeHeight, activeOffset, registerButtonRef } = useRailNavScroll(activeTab, navKeys);
|
|
119
145
|
const DRAWER_WIDTH = 220;
|
|
@@ -165,8 +191,8 @@ const OnyxMobileLayoutBase = ({ tabs, user, drawers = {}, onSignOut, onRefresh,
|
|
|
165
191
|
useEffect(() => {
|
|
166
192
|
if (currentTabConfig && currentTabConfig.subTabs.length > 0) {
|
|
167
193
|
const isValid = currentTabConfig.subTabs.some((st) => st.label === subTab);
|
|
168
|
-
if (!isValid)
|
|
169
|
-
|
|
194
|
+
if (!isValid && currentTabConfig.subTabs.length > 0)
|
|
195
|
+
navigateTo(undefined, currentTabConfig.subTabs[0].label);
|
|
170
196
|
}
|
|
171
197
|
}, [activeTab, tabs, subTab, currentTabConfig]);
|
|
172
198
|
const [[prevActive, direction], setDir] = useState([activeTab, 0]);
|
|
@@ -210,10 +236,7 @@ const OnyxMobileLayoutBase = ({ tabs, user, drawers = {}, onSignOut, onRefresh,
|
|
|
210
236
|
targetTab: tabs[0].id,
|
|
211
237
|
targetSubTab: st.label,
|
|
212
238
|
}))).map((item) => (_jsxs("button", { onClick: () => {
|
|
213
|
-
|
|
214
|
-
setActiveTab(item.targetTab);
|
|
215
|
-
if (item.targetSubTab)
|
|
216
|
-
setSubTab(item.targetSubTab);
|
|
239
|
+
navigateTo(item.targetTab, item.targetSubTab);
|
|
217
240
|
if (item.onClick)
|
|
218
241
|
item.onClick({
|
|
219
242
|
openDrawer: (id) => setActiveDrawer(id),
|
|
@@ -280,9 +303,9 @@ const OnyxMobileLayoutBase = ({ tabs, user, drawers = {}, onSignOut, onRefresh,
|
|
|
280
303
|
if (Math.abs(lastDxRef.current) > 50) {
|
|
281
304
|
const idx = subTabs.findIndex((t) => t.label === subTab);
|
|
282
305
|
if (lastDxRef.current < 0 && idx < subTabs.length - 1)
|
|
283
|
-
|
|
306
|
+
navigateTo(undefined, subTabs[idx + 1].label);
|
|
284
307
|
else if (lastDxRef.current > 0 && idx > 0)
|
|
285
|
-
|
|
308
|
+
navigateTo(undefined, subTabs[idx - 1].label);
|
|
286
309
|
}
|
|
287
310
|
}
|
|
288
311
|
if (isVerticalPull.current) {
|
|
@@ -365,7 +388,7 @@ const OnyxMobileLayoutBase = ({ tabs, user, drawers = {}, onSignOut, onRefresh,
|
|
|
365
388
|
setQuickMenu((prev) => ({ ...prev, visible: false, selectedIndex: -1 }));
|
|
366
389
|
}
|
|
367
390
|
else {
|
|
368
|
-
|
|
391
|
+
navigateTo(tabId);
|
|
369
392
|
}
|
|
370
393
|
};
|
|
371
394
|
const currentTranslate = isDragging ? renderDragX : isOpen ? DRAWER_WIDTH : 0;
|
|
@@ -377,7 +400,7 @@ const OnyxMobileLayoutBase = ({ tabs, user, drawers = {}, onSignOut, onRefresh,
|
|
|
377
400
|
? "none"
|
|
378
401
|
: "transform 0.3s cubic-bezier(0.32, 0.72, 0, 1), border-radius 0.3s",
|
|
379
402
|
overflow: "hidden",
|
|
380
|
-
}, children: [isOpen && !isDragging && (_jsx("div", { className: "absolute inset-0 z-50 bg-black/20", onClick: () => setIsOpen(false) })), _jsxs("div", { className: "flex-1 flex flex-col h-full mr-12 relative bg-[rgb(var(--bg-main))]", children: [_jsx(Header, { title: currentTabConfig?.navTitle || currentTabConfig?.label || "App", onMenuClick: () => setIsOpen(!isOpen), rightAction: currentTabConfig?.rightAction || rightAction }), _jsx(HorizontalTabs, { tabs: subTabs, active: subTab, onChange:
|
|
403
|
+
}, children: [isOpen && !isDragging && (_jsx("div", { className: "absolute inset-0 z-50 bg-black/20", onClick: () => setIsOpen(false) })), _jsxs("div", { className: "flex-1 flex flex-col h-full mr-12 relative bg-[rgb(var(--bg-main))]", children: [_jsx(Header, { title: currentTabConfig?.navTitle || currentTabConfig?.label || "App", onMenuClick: () => setIsOpen(!isOpen), rightAction: currentTabConfig?.rightAction || rightAction }), _jsx(HorizontalTabs, { tabs: subTabs, active: subTab, onChange: (label) => navigateTo(undefined, label) }), _jsxs("main", { ref: mainScrollRef, className: "flex-1 overflow-y-auto no-scrollbar overscroll-contain relative mr-4", children: [_jsx("div", { className: "absolute top-0 left-0 right-0 flex justify-center items-center pointer-events-none z-0", style: {
|
|
381
404
|
height: `${pullY}px`,
|
|
382
405
|
opacity: Math.min(pullY / 40, 1),
|
|
383
406
|
}, children: _jsx("div", { className: `p-2 bg-[rgb(var(--bg-surface))] rounded-full shadow-md border border-[rgb(var(--color-border-subtle))] ${isRefreshing
|
package/dist/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface SubTabConfig {
|
|
|
3
3
|
label: string;
|
|
4
4
|
icon: ComponentType<any>;
|
|
5
5
|
view: ComponentType<any>;
|
|
6
|
+
path?: string;
|
|
6
7
|
}
|
|
7
8
|
export interface QuickActionConfig {
|
|
8
9
|
label: string;
|
|
@@ -16,6 +17,7 @@ export interface TabConfig {
|
|
|
16
17
|
label: string;
|
|
17
18
|
icon: ComponentType<any>;
|
|
18
19
|
navTitle?: string;
|
|
20
|
+
path?: string;
|
|
19
21
|
subTabs: SubTabConfig[];
|
|
20
22
|
quickActions: QuickActionConfig[];
|
|
21
23
|
rightAction?: ReactNode;
|
|
@@ -50,5 +52,8 @@ export interface OnyxMobileLayoutProps {
|
|
|
50
52
|
isRefreshing?: boolean;
|
|
51
53
|
rightAction?: ReactNode;
|
|
52
54
|
initialTab?: string;
|
|
55
|
+
activeTab?: string;
|
|
56
|
+
activeSubTab?: string;
|
|
57
|
+
onNavigate?: (path: string) => void;
|
|
53
58
|
drawerItems?: DrawerItemConfig[];
|
|
54
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kawaiininja/layouts",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "High-performance, premium mobile-first layouts for the Onyx Framework, featuring gesture-driven navigation, radial quick actions, and integrated theme support.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "https://github.com/
|
|
29
|
+
"url": "https://github.com/4kawaiininja/onyx-framework.git",
|
|
30
30
|
"directory": "layouts"
|
|
31
31
|
},
|
|
32
32
|
"bugs": {
|
|
33
|
-
"url": "https://github.com/
|
|
33
|
+
"url": "https://github.com/4kawaiininja/onyx-framework/issues"
|
|
34
34
|
},
|
|
35
|
-
"homepage": "https://github.com/
|
|
35
|
+
"homepage": "https://github.com/4kawaiininja/onyx-framework/blob/main/layouts/README.md",
|
|
36
36
|
"exports": {
|
|
37
37
|
".": {
|
|
38
38
|
"types": "./dist/index.d.ts",
|