@treenity/react 3.0.4 → 3.0.7

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 (101) hide show
  1. package/dist/AclEditor.js +3 -3
  2. package/dist/ActionCards.js +5 -5
  3. package/dist/App.d.ts.map +1 -1
  4. package/dist/App.js +9 -8
  5. package/dist/App.js.map +1 -1
  6. package/dist/CLAUDE.md +16 -0
  7. package/dist/ComponentSection.js +4 -4
  8. package/dist/ComponentSection.js.map +1 -1
  9. package/dist/ErrorBoundary.js +1 -1
  10. package/dist/Inspector.css +54 -0
  11. package/dist/Inspector.js +9 -9
  12. package/dist/Inspector.js.map +1 -1
  13. package/dist/Login.js +4 -4
  14. package/dist/NodeEditor.d.ts.map +1 -1
  15. package/dist/NodeEditor.js +10 -9
  16. package/dist/NodeEditor.js.map +1 -1
  17. package/dist/Tree.css +91 -0
  18. package/dist/Tree.js +3 -3
  19. package/dist/Treenity.d.ts +0 -1
  20. package/dist/Treenity.d.ts.map +1 -1
  21. package/dist/Treenity.js +1 -1
  22. package/dist/Treenity.js.map +1 -1
  23. package/dist/ViewPage.js +1 -1
  24. package/dist/bind/engine.js +2 -2
  25. package/dist/bind/hook.js +2 -2
  26. package/dist/components/ConfirmDialog.js +1 -1
  27. package/dist/components/ConfirmPopover.js +2 -2
  28. package/dist/components/PathBreadcrumb.js +1 -1
  29. package/dist/components/lib/utils.ts.bak +6 -0
  30. package/dist/components/ui/accordion.js +1 -1
  31. package/dist/components/ui/alert-dialog.js +2 -2
  32. package/dist/components/ui/badge.js +1 -1
  33. package/dist/components/ui/breadcrumb.js +1 -1
  34. package/dist/components/ui/button.js +1 -1
  35. package/dist/components/ui/card.js +1 -1
  36. package/dist/components/ui/checkbox.js +1 -1
  37. package/dist/components/ui/command.js +2 -2
  38. package/dist/components/ui/dialog.js +2 -2
  39. package/dist/components/ui/drawer.js +1 -1
  40. package/dist/components/ui/dropdown-menu.js +1 -1
  41. package/dist/components/ui/form-field.js +1 -1
  42. package/dist/components/ui/input.js +1 -1
  43. package/dist/components/ui/label.js +1 -1
  44. package/dist/components/ui/pagination.js +2 -2
  45. package/dist/components/ui/popover.js +1 -1
  46. package/dist/components/ui/progress.js +1 -1
  47. package/dist/components/ui/resizable.js +1 -1
  48. package/dist/components/ui/scroll-area.js +1 -1
  49. package/dist/components/ui/select.js +1 -1
  50. package/dist/components/ui/separator.js +1 -1
  51. package/dist/components/ui/sheet.js +1 -1
  52. package/dist/components/ui/skeleton.js +1 -1
  53. package/dist/components/ui/slider.js +1 -1
  54. package/dist/components/ui/switch.js +1 -1
  55. package/dist/components/ui/table.js +1 -1
  56. package/dist/components/ui/tabs.js +1 -1
  57. package/dist/components/ui/textarea.js +1 -1
  58. package/dist/components/ui/toggle-group.js +2 -2
  59. package/dist/components/ui/toggle.js +1 -1
  60. package/dist/components/ui/tooltip.js +1 -1
  61. package/dist/context/index.js +2 -2
  62. package/dist/lib/minimd.css +28 -0
  63. package/dist/mods/editor-ui/CLAUDE.md +3 -0
  64. package/dist/mods/editor-ui/DraftTextarea.d.ts +8 -0
  65. package/dist/mods/editor-ui/DraftTextarea.d.ts.map +1 -0
  66. package/dist/mods/editor-ui/DraftTextarea.js +23 -0
  67. package/dist/mods/editor-ui/DraftTextarea.js.map +1 -0
  68. package/dist/mods/editor-ui/FieldLabel.js +2 -2
  69. package/dist/mods/editor-ui/default-edit.js +3 -3
  70. package/dist/mods/editor-ui/default-view.js +4 -4
  71. package/dist/mods/editor-ui/dir-view.js +2 -2
  72. package/dist/mods/editor-ui/editor-ui.css +174 -0
  73. package/dist/mods/editor-ui/empty-placeholder.js +2 -2
  74. package/dist/mods/editor-ui/form-field.js +5 -5
  75. package/dist/mods/editor-ui/form-field.js.map +1 -1
  76. package/dist/mods/editor-ui/form-fields.d.ts.map +1 -1
  77. package/dist/mods/editor-ui/form-fields.js +17 -16
  78. package/dist/mods/editor-ui/form-fields.js.map +1 -1
  79. package/dist/mods/editor-ui/layout-view.js +2 -2
  80. package/dist/mods/editor-ui/list-items.js +1 -1
  81. package/dist/mods/editor-ui/node-utils.js +1 -1
  82. package/dist/mods/editor-ui/node-utils.js.map +1 -1
  83. package/dist/mods/editor-ui/type-picker.js +5 -5
  84. package/dist/mods/treenity/CLAUDE.md +7 -0
  85. package/dist/mods/treenity/groups/index.js +3 -3
  86. package/dist/mods/treenity/preview.js +2 -2
  87. package/dist/mods/treenity/ref-view.js +3 -3
  88. package/dist/root.css +117 -0
  89. package/dist/trpc.d.ts +8 -0
  90. package/dist/trpc.d.ts.map +1 -1
  91. package/package.json +2 -2
  92. package/src/App.tsx +3 -2
  93. package/src/ComponentSection.tsx +1 -1
  94. package/src/Inspector.tsx +2 -2
  95. package/src/NodeEditor.tsx +3 -2
  96. package/src/Treenity.tsx +1 -1
  97. package/src/mods/editor-ui/DraftTextarea.tsx +42 -0
  98. package/src/mods/editor-ui/form-field.tsx +4 -4
  99. package/src/mods/editor-ui/form-fields.tsx +11 -10
  100. package/src/mods/editor-ui/node-utils.ts +1 -1
  101. package/vite-plugin-treenity.ts +7 -1
package/dist/root.css ADDED
@@ -0,0 +1,117 @@
1
+ /* Tailwind disabled — using CDN in index.html to avoid cross-package conflict */
2
+ /* @import 'tailwindcss'; */
3
+ /* @source "."; */
4
+ /* @source "../../src/mods"; */
5
+
6
+ /* Local fonts */
7
+ @font-face { font-family: 'Manrope'; src: url('/fonts/Manrope-Light.ttf'); font-weight: 300; font-display: swap; }
8
+ @font-face { font-family: 'Manrope'; src: url('/fonts/Manrope-Regular.ttf'); font-weight: 400; font-display: swap; }
9
+ @font-face { font-family: 'Manrope'; src: url('/fonts/Manrope-Medium.ttf'); font-weight: 500; font-display: swap; }
10
+ @font-face { font-family: 'Manrope'; src: url('/fonts/Manrope-SemiBold.ttf'); font-weight: 600; font-display: swap; }
11
+
12
+ /* shadcn/ui theme tokens — now in CDN (index.html), kept here for reference */
13
+ /* @theme inline extend { ... } — see index.html <style type="text/tailwindcss"> */
14
+
15
+ /* Tailwind theme tokens — unlayered :root beats CDN's layered output */
16
+ :root {
17
+ --color-background: #0d1117;
18
+ --color-foreground: #e6edf3;
19
+ --color-card: #161b22;
20
+ --color-card-foreground: #e6edf3;
21
+ --color-popover: #161b22;
22
+ --color-popover-foreground: #e6edf3;
23
+ --color-primary: #2ecc71;
24
+ --color-primary-foreground: #ffffff;
25
+ --color-secondary: #21262d;
26
+ --color-secondary-foreground: #e6edf3;
27
+ --color-muted: #161b22;
28
+ --color-muted-foreground: #7d8590;
29
+ --color-accent: rgba(46, 204, 113, 0.15);
30
+ --color-accent-foreground: #2ecc71;
31
+ --color-destructive: #f85149;
32
+ --color-destructive-foreground: #e6edf3;
33
+ --color-border: #30363d;
34
+ --color-input: #30363d;
35
+ --color-ring: #2ecc71;
36
+ --radius-sm: 4px;
37
+ --radius-md: 6px;
38
+ --radius-lg: 8px;
39
+
40
+ /* Legacy aliases */
41
+ --bg: var(--color-background);
42
+ --surface: var(--color-card);
43
+ --surface-2: var(--color-secondary);
44
+ --surface-3: var(--color-border);
45
+ --border: var(--color-border);
46
+ --border-subtle: var(--color-border);
47
+ --text: var(--color-foreground);
48
+ --text-2: var(--color-muted-foreground);
49
+ --text-3: color-mix(in srgb, var(--color-muted-foreground) 60%, transparent);
50
+ --accent: var(--color-primary);
51
+ --accent-subtle: var(--color-accent);
52
+ --danger: var(--color-destructive);
53
+ --danger-subtle: color-mix(in srgb, var(--color-destructive) 15%, transparent);
54
+ --success: #3fb950;
55
+ --success-subtle: rgba(63, 185, 80, 0.15);
56
+ --warning: #d29922;
57
+ --radius: var(--radius-md);
58
+ --radius-lg: var(--radius-lg);
59
+ --font: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
60
+ --mono: 'SF Mono', SFMono-Regular, 'Cascadia Code', 'Fira Code', Consolas, monospace;
61
+ --transition: 0.15s ease;
62
+ }
63
+ @layer base {
64
+ *,
65
+ ::before,
66
+ ::after {
67
+ margin: 0;
68
+ padding: 0;
69
+ box-sizing: border-box;
70
+ border-color: var(--color-border);
71
+ }
72
+ }
73
+ html,
74
+ body {
75
+ height: 100%;
76
+ font-family: var(--font);
77
+ }
78
+ body {
79
+ background: var(--bg);
80
+ color: var(--text);
81
+ font-size: 14px;
82
+ line-height: 1.5;
83
+ }
84
+
85
+ @layer base {
86
+ input,
87
+ textarea,
88
+ select {
89
+ width: 100%;
90
+ padding: 4px 8px;
91
+ background: var(--bg);
92
+ border: 1px solid var(--border);
93
+ border-radius: var(--radius);
94
+ color: var(--text);
95
+ font-size: 12px;
96
+ font-family: var(--mono);
97
+ outline: none;
98
+ transition: border-color var(--transition);
99
+ }
100
+ input:focus,
101
+ textarea:focus,
102
+ select:focus {
103
+ border-color: var(--accent);
104
+ }
105
+ input:read-only {
106
+ color: var(--text-2);
107
+ cursor: default;
108
+ }
109
+ input:read-only:focus {
110
+ border-color: var(--border);
111
+ }
112
+ textarea {
113
+ min-height: 80px;
114
+ resize: vertical;
115
+ line-height: 1.5;
116
+ }
117
+ }
package/dist/trpc.d.ts CHANGED
@@ -174,6 +174,14 @@ export declare const trpc: import("@trpc/client").TRPCClient<import("@trpc/serve
174
174
  };
175
175
  meta: object;
176
176
  }>;
177
+ devLogin: import("@trpc/server").TRPCMutationProcedure<{
178
+ input: void;
179
+ output: {
180
+ token: string;
181
+ userId: string;
182
+ };
183
+ meta: object;
184
+ }>;
177
185
  streamAction: import("@trpc/server/unstable-core-do-not-import").LegacyObservableSubscriptionProcedure<{
178
186
  input: {
179
187
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../src/trpc.ts"],"names":[],"mappings":"AAIA,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAExC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,QAErC;AAED,wBAAgB,UAAU,SAEzB;AAED,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AAUtD,eAAO,MAAQ,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAiE,CAAC"}
1
+ {"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../src/trpc.ts"],"names":[],"mappings":"AAIA,wBAAgB,QAAQ,IAAI,MAAM,GAAG,IAAI,CAExC;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,QAErC;AAED,wBAAgB,UAAU,SAEzB;AAED,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AAUtD,eAAO,MAAQ,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAiE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treenity/react",
3
- "version": "3.0.4",
3
+ "version": "3.0.7",
4
4
  "description": "React binding for Treenity — reactive hooks, admin UI, and context-based component rendering.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -80,7 +80,7 @@
80
80
  "README.md"
81
81
  ],
82
82
  "scripts": {
83
- "build": "tsc -p tsconfig.build.json",
83
+ "build": "tsc -p tsconfig.build.json && fix-hash-imports --copy-assets",
84
84
  "prepack": "npm run build",
85
85
  "test": "tsx --conditions development --test src/**/*.test.ts",
86
86
  "dev": "vite"
package/src/App.tsx CHANGED
@@ -42,7 +42,8 @@ export function App() {
42
42
  const token = getToken();
43
43
  if (!token) {
44
44
  try {
45
- const { token: anonToken, userId } = await trpc.anonLogin.mutate();
45
+ const login = import.meta.env.VITE_DEV_LOGIN ? trpc.devLogin : trpc.anonLogin;
46
+ const { token: anonToken, userId } = await login.mutate();
46
47
  setToken(anonToken);
47
48
  setAuthed(userId);
48
49
  setAuthChecked(true);
@@ -439,7 +440,7 @@ export function App() {
439
440
  if (!authed) return <LoginScreen onLogin={(uid) => setAuthed(uid)} />;
440
441
 
441
442
  const isAnon = authed.startsWith('anon:');
442
- const needsLogin = isAnon || showLoginModal;
443
+ const needsLogin = showLoginModal;
443
444
  if (mode === 'view') return <NavigateProvider value={navigate}><ViewPage path={viewPath} /></NavigateProvider>;
444
445
  if (mode === 'preview') return <NavigateProvider value={navigate}><ViewPage path={viewPath} editorLink /></NavigateProvider>;
445
446
 
@@ -96,7 +96,7 @@ export function ComponentSection({
96
96
  </div>
97
97
 
98
98
  {!collapsed && (
99
- <ErrorBoundary>
99
+ <ErrorBoundary key={`${node.$path}:${compType}`}>
100
100
  <EditPanel node={node} type={compType} data={data} onData={onData} />
101
101
  <ActionCardList
102
102
  path={node.$path}
package/src/Inspector.tsx CHANGED
@@ -28,7 +28,7 @@ export function Inspector({ path, currentUserId, onDelete, onAddComponent, onSel
28
28
  const node = usePath(path);
29
29
  const [confirmDelete, setConfirmDelete] = useState(false);
30
30
  const [editing, setEditing] = useState(false);
31
- const [context, setContext] = useState('react');
31
+ const [context, setContext] = useState('react:layout');
32
32
 
33
33
  // Reset context when path changes
34
34
  const [prevPath, setPrevPath] = useState(path);
@@ -113,7 +113,7 @@ export function Inspector({ path, currentUserId, onDelete, onAddComponent, onSel
113
113
  {/* Rendered view */}
114
114
  <ScrollArea className="flex-1">
115
115
  <div className="p-4">
116
- <ErrorBoundary>
116
+ <ErrorBoundary key={node.$path}>
117
117
  <RenderContext name={context}>
118
118
  <div className="node-view">
119
119
  <Render value={node} />
@@ -7,6 +7,7 @@ import { Input } from '#components/ui/input';
7
7
  import { ScrollArea } from '#components/ui/scroll-area';
8
8
  import { Tabs, TabsList, TabsTrigger } from '#components/ui/tabs';
9
9
  import { toPlain } from '#lib/to-plain';
10
+ import { DraftTextarea } from '#mods/editor-ui/DraftTextarea';
10
11
  import { FieldLabel, RefEditor } from '#mods/editor-ui/FieldLabel';
11
12
  import { getComponents, getPlainFields, getSchema } from '#mods/editor-ui/node-utils';
12
13
  import { type ComponentData, type GroupPerm, isRef, type NodeData, resolve } from '@treenity/core';
@@ -272,9 +273,9 @@ export function NodeEditor({ node, open, onClose, currentUserId, toast, onAddCom
272
273
  )}
273
274
  </>
274
275
  ) : (
275
- <textarea
276
+ <DraftTextarea
276
277
  value={snap.jsonText}
277
- onChange={(e) => { st.jsonText = e.target.value; st.dirty = true; }}
278
+ onChange={(text) => { st.jsonText = text; st.dirty = true; }}
278
279
  spellCheck={false}
279
280
  />
280
281
  )}
package/src/Treenity.tsx CHANGED
@@ -4,7 +4,7 @@ import { type ReactNode } from 'react'
4
4
  import { App } from './App'
5
5
  import './load-client'
6
6
  import { Toaster } from './components/ui/sonner'
7
- import './root.css'
7
+ // CSS must be imported by the consumer: import '@treenity/react/root.css'
8
8
 
9
9
  enablePatches()
10
10
 
@@ -0,0 +1,42 @@
1
+ // Uncontrolled textarea that preserves cursor position during external re-renders.
2
+ // Uses ref + defaultValue so React never touches the DOM value while user is editing.
3
+
4
+ import { Textarea } from '#components/ui/textarea';
5
+ import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
6
+
7
+ type Props = Omit<React.ComponentProps<typeof Textarea>, 'value' | 'defaultValue' | 'onChange'> & {
8
+ value: string;
9
+ onChange: (text: string) => void;
10
+ };
11
+
12
+ export const DraftTextarea = forwardRef<HTMLTextAreaElement, Props>(
13
+ ({ value, onChange, ...props }, fwd) => {
14
+ const ref = useRef<HTMLTextAreaElement>(null);
15
+ const editing = useRef(false);
16
+
17
+ useImperativeHandle(fwd, () => ref.current!);
18
+
19
+ // Sync external value into DOM only when not focused
20
+ useEffect(() => {
21
+ if (!editing.current && ref.current && ref.current.value !== value) {
22
+ ref.current.value = value;
23
+ }
24
+ }, [value]);
25
+
26
+ return (
27
+ <Textarea
28
+ ref={ref}
29
+ defaultValue={value}
30
+ onFocus={() => { editing.current = true; }}
31
+ onBlur={() => {
32
+ editing.current = false;
33
+ if (ref.current && ref.current.value !== value) {
34
+ ref.current.value = value;
35
+ }
36
+ }}
37
+ onChange={(e) => onChange(e.target.value)}
38
+ {...props}
39
+ />
40
+ );
41
+ },
42
+ );
@@ -1,6 +1,6 @@
1
1
  import { Button } from '#components/ui/button';
2
2
  import { Input } from '#components/ui/input';
3
- import { Textarea } from '#components/ui/textarea';
3
+ import { DraftTextarea } from '#mods/editor-ui/DraftTextarea';
4
4
  import { isRef, resolveExact } from '@treenity/core';
5
5
  import { createElement, useState } from 'react';
6
6
  import { FieldLabel, RefEditor } from './FieldLabel';
@@ -92,12 +92,12 @@ export function StringArrayField({
92
92
 
93
93
  if (!isStrings) {
94
94
  return (
95
- <Textarea
95
+ <DraftTextarea
96
96
  className="min-h-16 text-xs font-mono"
97
97
  value={JSON.stringify(value, null, 2)}
98
- onChange={(e) => {
98
+ onChange={(text) => {
99
99
  try {
100
- onChange(JSON.parse(e.target.value));
100
+ onChange(JSON.parse(text));
101
101
  } catch {
102
102
  /* typing */
103
103
  }
@@ -5,6 +5,7 @@ import { tree as clientStore } from '#client';
5
5
  // Covers: string, text, textarea, number, integer, boolean, array, object, image, uri, url, select, timestamp, path
6
6
  import { Button } from '#components/ui/button';
7
7
  import { Input } from '#components/ui/input';
8
+ import { DraftTextarea } from '#mods/editor-ui/DraftTextarea';
8
9
  import { Popover, PopoverContent, PopoverTrigger } from '#components/ui/popover';
9
10
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '#components/ui/select';
10
11
  import { Switch } from '#components/ui/switch';
@@ -281,13 +282,13 @@ function ObjectForm({ value, onChange }: FP) {
281
282
  return (
282
283
  <div className="rounded border border-border/50 bg-muted/30 p-2">
283
284
  {modeToggle}
284
- <Textarea
285
+ <DraftTextarea
285
286
  className={`text-[11px] min-h-[60px] ${jsonError ? 'border-destructive' : ''}`}
286
287
  value={jsonDraft}
287
- onChange={(e) => {
288
- setJsonDraft(e.target.value);
288
+ onChange={(text) => {
289
+ setJsonDraft(text);
289
290
  try {
290
- const parsed = JSON.parse(e.target.value);
291
+ const parsed = JSON.parse(text);
291
292
  if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
292
293
  emit(parsed);
293
294
  setJsonError(false);
@@ -334,12 +335,12 @@ function ObjectForm({ value, onChange }: FP) {
334
335
  onChange={(e) => emit({ ...obj, [k]: e.target.value })}
335
336
  />
336
337
  ) : (
337
- <Textarea
338
+ <DraftTextarea
338
339
  className="flex-1 min-w-0 text-[11px] font-mono min-h-[40px]"
339
340
  value={JSON.stringify(v, null, 2)}
340
- onChange={(e) => {
341
+ onChange={(text) => {
341
342
  try {
342
- emit({ ...obj, [k]: JSON.parse(e.target.value) });
343
+ emit({ ...obj, [k]: JSON.parse(text) });
343
344
  } catch {
344
345
  /* typing */
345
346
  }
@@ -484,11 +485,11 @@ function ArrayForm({ value, onChange }: FP) {
484
485
 
485
486
  // object/other — textarea fallback
486
487
  return (
487
- <Textarea
488
+ <DraftTextarea
488
489
  value={JSON.stringify(arr, null, 2)}
489
- onChange={(e) => {
490
+ onChange={(text) => {
490
491
  try {
491
- emit(JSON.parse(e.target.value));
492
+ emit(JSON.parse(text));
492
493
  } catch {
493
494
  /* let user keep typing */
494
495
  }
@@ -79,5 +79,5 @@ export function getActionSchema(type: string, action: string): TypeSchema | null
79
79
  }
80
80
 
81
81
  export function pickDefaultContext(_type: string): string {
82
- return 'react';
82
+ return 'react:layout';
83
83
  }
@@ -189,7 +189,13 @@ export default function treenityPlugin(opts?: { modsDirs?: string[] }): Plugin {
189
189
  // Resolve # imports via nearest package.json imports field
190
190
  if (id.startsWith('#')) {
191
191
  const pkg = readPkg(dirname(importer));
192
- if (pkg?.imports) return matchPattern(id, pkg.imports, pkg.dir, conditions);
192
+ if (pkg?.imports) {
193
+ // Inside node_modules: skip 'development' condition to stay in dist/
194
+ // (dist files use relative ./hooks, # must resolve to same dist files)
195
+ const isNm = importer.includes('/node_modules/');
196
+ const conds = isNm ? conditions.filter(c => c !== 'development') : conditions;
197
+ return matchPattern(id, pkg.imports, pkg.dir, conds);
198
+ }
193
199
  }
194
200
 
195
201
  // Resolve @treenity/* exports (Vite doesn't handle array conditions)