@geomak/ui 7.17.0 → 7.18.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.
- package/README.md +67 -2
- package/dist/index.cjs +52 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +52 -7
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# @geomak/ui · Oxygen Design System
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**100+ production-grade React components for enterprise apps** — dashboards, CRMs, internal tools, and landing pages. Token-driven, accessible, light/dark first-class, and properly tree-shakeable.
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/@geomak/ui)
|
|
10
10
|
[](https://www.npmjs.com/package/@geomak/ui)
|
|
@@ -64,7 +64,7 @@ import { ChevronDown, Search, createIcon } from '@geomak/ui/icons'
|
|
|
64
64
|
|
|
65
65
|
## Components
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
100+ components across these groups — all with **live controls and a written guide** in [Storybook](https://oxygenui.com).
|
|
68
68
|
|
|
69
69
|
| Group | Components |
|
|
70
70
|
|---|---|
|
|
@@ -135,6 +135,71 @@ The standard **gray / slate / zinc** ramps and **black** stay available alongsid
|
|
|
135
135
|
|
|
136
136
|
---
|
|
137
137
|
|
|
138
|
+
## AI toolchain (Claude Code)
|
|
139
|
+
|
|
140
|
+
OxygenUI ships a built-in MCP server and a set of Claude Code skills and commands that let AI assistants look up component APIs, search by use-case, and scaffold new components — all grounded in the real MDX documentation.
|
|
141
|
+
|
|
142
|
+
### MCP server
|
|
143
|
+
|
|
144
|
+
The server is deployed alongside Storybook on Netlify as a Netlify Function at `/mcp`. It exposes four tools:
|
|
145
|
+
|
|
146
|
+
| Tool | What it does |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `list_components` | Browse all 100+ components with slug, category, and description |
|
|
149
|
+
| `get_component` | Fetch the full props API and usage examples for one component by slug |
|
|
150
|
+
| `find_component` | Keyword search across names, categories, and descriptions |
|
|
151
|
+
| `get_token` | Look up CSS custom properties by name or category prefix |
|
|
152
|
+
|
|
153
|
+
**Connect it:**
|
|
154
|
+
|
|
155
|
+
```jsonc
|
|
156
|
+
// .mcp.json (already in the repo — update the URL after first deploy)
|
|
157
|
+
{
|
|
158
|
+
"mcpServers": {
|
|
159
|
+
"oxygen-ui": {
|
|
160
|
+
"type": "http",
|
|
161
|
+
"url": "https://your-site.netlify.app/mcp"
|
|
162
|
+
},
|
|
163
|
+
"oxygen-ui-local": {
|
|
164
|
+
"type": "http",
|
|
165
|
+
"url": "http://localhost:8888/mcp"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Or add it from the CLI: `claude mcp add oxygen-ui --transport http https://your-site.netlify.app/mcp`
|
|
172
|
+
|
|
173
|
+
### Claude Code commands
|
|
174
|
+
|
|
175
|
+
Four slash commands are registered in `.claude/commands/`:
|
|
176
|
+
|
|
177
|
+
| Command | Usage |
|
|
178
|
+
|---|---|
|
|
179
|
+
| `/ui-lookup <name>` | Fetch the full docs for a component — e.g. `/ui-lookup wizard` |
|
|
180
|
+
| `/ui-find <query>` | Search by use-case — e.g. `/ui-find virtualized table` |
|
|
181
|
+
| `/ui-scaffold <name>` | Scaffold a new component with source + story + MDX guide |
|
|
182
|
+
| `/ui-story <name>` | Add story coverage for an existing component |
|
|
183
|
+
|
|
184
|
+
### Context skill
|
|
185
|
+
|
|
186
|
+
The `/oxygen-ui` skill loads the full design system context into any Claude Code session — import patterns, token usage, Tailwind utilities, form API, and repository conventions. Invoke it at the start of any session where you plan to build against `@geomak/ui`.
|
|
187
|
+
|
|
188
|
+
### Local development with the MCP server
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm install -g netlify-cli # one-time global install
|
|
192
|
+
yarn mcp:dev # generates manifest + starts netlify dev on port 8888
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The function is then available at `http://localhost:8888/mcp`. Claude Code picks up the `oxygen-ui-local` entry from `.mcp.json` automatically when you open the project.
|
|
196
|
+
|
|
197
|
+
### How the manifest is built
|
|
198
|
+
|
|
199
|
+
At build time, `scripts/generate-ai-manifest.mjs` scans all 101 MDX guide files in `src/docs/` and the co-located MDX files in `src/components/`, strips Storybook boilerplate, and emits `netlify/functions/ai-manifest.json`. The Netlify Function bundles this JSON via esbuild — no database, no runtime file I/O, no cold-start penalty.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
138
203
|
## Development
|
|
139
204
|
|
|
140
205
|
```bash
|
package/dist/index.cjs
CHANGED
|
@@ -5092,12 +5092,52 @@ function useTargetBbox(ref) {
|
|
|
5092
5092
|
}
|
|
5093
5093
|
var TOOLTIP_WIDTH = 280;
|
|
5094
5094
|
var TOOLTIP_GAP = 12;
|
|
5095
|
-
function tooltipStyleFor(bbox, placement) {
|
|
5096
|
-
const
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5095
|
+
function tooltipStyleFor(bbox, placement, tooltipHeight) {
|
|
5096
|
+
const GAP2 = TOOLTIP_GAP;
|
|
5097
|
+
const W = TOOLTIP_WIDTH;
|
|
5098
|
+
const H = tooltipHeight;
|
|
5099
|
+
const vw = window.innerWidth;
|
|
5100
|
+
const vh = window.innerHeight;
|
|
5101
|
+
let side = placement ?? "right";
|
|
5102
|
+
if (side === "right" && bbox.right + GAP2 + W > vw) side = "left";
|
|
5103
|
+
if (side === "left" && bbox.left - W - GAP2 < 0) side = "right";
|
|
5104
|
+
if (side === "bottom" && bbox.bottom + GAP2 + H > vh) side = "top";
|
|
5105
|
+
if (side === "top" && bbox.top - GAP2 - H < 0) side = "bottom";
|
|
5106
|
+
let left;
|
|
5107
|
+
let top;
|
|
5108
|
+
let transform;
|
|
5109
|
+
if (side === "right") {
|
|
5110
|
+
left = bbox.right + GAP2;
|
|
5111
|
+
top = bbox.top + bbox.height / 2;
|
|
5112
|
+
transform = "translateY(-50%)";
|
|
5113
|
+
} else if (side === "left") {
|
|
5114
|
+
left = bbox.left - W - GAP2;
|
|
5115
|
+
top = bbox.top + bbox.height / 2;
|
|
5116
|
+
transform = "translateY(-50%)";
|
|
5117
|
+
} else if (side === "bottom") {
|
|
5118
|
+
left = bbox.left + bbox.width / 2;
|
|
5119
|
+
top = bbox.bottom + GAP2;
|
|
5120
|
+
transform = "translateX(-50%)";
|
|
5121
|
+
} else {
|
|
5122
|
+
left = bbox.left + bbox.width / 2;
|
|
5123
|
+
top = bbox.top - GAP2;
|
|
5124
|
+
transform = "translate(-50%, -100%)";
|
|
5125
|
+
}
|
|
5126
|
+
if (side === "top" || side === "bottom") {
|
|
5127
|
+
left = Math.max(GAP2 + W / 2, Math.min(left, vw - W / 2 - GAP2));
|
|
5128
|
+
} else {
|
|
5129
|
+
left = Math.max(GAP2, Math.min(left, vw - W - GAP2));
|
|
5130
|
+
}
|
|
5131
|
+
if (H > 0) {
|
|
5132
|
+
if (side === "right" || side === "left") {
|
|
5133
|
+
top = Math.max(GAP2 + H / 2, Math.min(top, vh - GAP2 - H / 2));
|
|
5134
|
+
} else if (side === "bottom") {
|
|
5135
|
+
top = Math.max(GAP2, Math.min(top, vh - GAP2 - H));
|
|
5136
|
+
} else {
|
|
5137
|
+
top = Math.max(GAP2 + H, Math.min(top, vh - GAP2));
|
|
5138
|
+
}
|
|
5139
|
+
}
|
|
5140
|
+
return { left, top, transform, width: W };
|
|
5101
5141
|
}
|
|
5102
5142
|
function useFocusTrap(containerRef, active) {
|
|
5103
5143
|
React36.useEffect(() => {
|
|
@@ -5145,6 +5185,11 @@ function Wizard({
|
|
|
5145
5185
|
const reduced = framerMotion.useReducedMotion();
|
|
5146
5186
|
const [open, setOpen] = React36.useState(() => steps.length > 0 && !readDismissed(storageKey));
|
|
5147
5187
|
const [activeIndex, setActiveIndex] = React36.useState(0);
|
|
5188
|
+
const [tooltipHeight, setTooltipHeight] = React36.useState(0);
|
|
5189
|
+
React36.useLayoutEffect(() => {
|
|
5190
|
+
const h = tooltipRef.current?.offsetHeight ?? 0;
|
|
5191
|
+
if (h > 0) setTooltipHeight((prev) => prev === h ? prev : h);
|
|
5192
|
+
}, [activeIndex, open]);
|
|
5148
5193
|
const step = steps[activeIndex];
|
|
5149
5194
|
const bbox = useTargetBbox(step?.stepRef);
|
|
5150
5195
|
useFocusTrap(tooltipRef, open);
|
|
@@ -5197,7 +5242,7 @@ function Wizard({
|
|
|
5197
5242
|
right: 0,
|
|
5198
5243
|
height: bbox.height + SPOT_PAD * 2
|
|
5199
5244
|
} : { display: "none" };
|
|
5200
|
-
const tooltipStyle = bbox ? tooltipStyleFor(bbox, step?.placement) : { display: "none" };
|
|
5245
|
+
const tooltipStyle = bbox ? tooltipStyleFor(bbox, step?.placement, tooltipHeight) : { display: "none" };
|
|
5201
5246
|
const isLast = activeIndex === steps.length - 1;
|
|
5202
5247
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5203
5248
|
children,
|