@close-by/clay 0.1.0 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@close-by/clay",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "NiceGUI-like ui.* facade for Clay apps",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,34 @@
1
+ import { existsSync } from 'fs';
2
+ import { createRequire } from 'module';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ /**
7
+ * Resolve Vite client assets for `ui.run({ clientDir })`.
8
+ *
9
+ * Preference order:
10
+ * 1. Explicit `fromDir` (tests / advanced)
11
+ * 2. `@close-by/clay-cli/client-dist` when the CLI package is installed
12
+ * 3. Monorepo `packages/client/dist` (dev) relative to this module
13
+ *
14
+ * Prefer this over hardcoding `node_modules/@close-by/clay-cli/client-dist`.
15
+ */
16
+ export function resolveClayClientDir(fromDir?: string): string {
17
+ if (fromDir) {
18
+ const shipped = join(fromDir, '../client-dist');
19
+ if (existsSync(join(shipped, 'index.html'))) return shipped;
20
+ return join(fromDir, '../../client/dist');
21
+ }
22
+
23
+ try {
24
+ const require = createRequire(import.meta.url);
25
+ const pkgJson = require.resolve('@close-by/clay-cli/package.json');
26
+ const shipped = join(dirname(pkgJson), 'client-dist');
27
+ if (existsSync(join(shipped, 'index.html'))) return shipped;
28
+ } catch {
29
+ // CLI not installed — fall through
30
+ }
31
+
32
+ // Monorepo: packages/ui/src → packages/client/dist
33
+ return join(fileURLToPath(new URL('.', import.meta.url)), '../../client/dist');
34
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  button as buttonFactory,
3
+ iconButton as iconButtonFactory,
3
4
  label as labelFactory,
4
5
  input as inputFactory,
5
6
  checkbox as checkboxFactory,
@@ -14,6 +15,7 @@ import {
14
15
  slider as sliderFactory,
15
16
  textArea as textAreaFactory,
16
17
  link as linkFactory,
18
+ externalLink as externalLinkFactory,
17
19
  badge as badgeFactory,
18
20
  alert as alertFactory,
19
21
  spinner as spinnerFactory,
@@ -84,6 +86,9 @@ import {
84
86
  ai as aiNamespace,
85
87
  AiChatElement,
86
88
  type ButtonProps,
89
+ type IconButtonProps,
90
+ type ButtonVariant,
91
+ type ButtonSize,
87
92
  type LabelProps,
88
93
  type InputProps,
89
94
  type CheckboxProps,
@@ -190,6 +195,7 @@ import {
190
195
  type ChooseOption,
191
196
  type AppProps,
192
197
  type AppNavItem,
198
+ type AppPrimaryAction,
193
199
  type AppUser,
194
200
  type MarkdownProps,
195
201
  type HtmlProps,
@@ -332,6 +338,9 @@ import {
332
338
  download as downloadCore,
333
339
  clipboard as clipboardCore,
334
340
  runJavaScript as runJavaScriptCore,
341
+ getUrlHash as getUrlHashCore,
342
+ setUrlHash as setUrlHashCore,
343
+ openExternal as openExternalCore,
335
344
  scroll as scrollCore,
336
345
  timer as timerCore,
337
346
  storage as storageCore,
@@ -373,8 +382,20 @@ import {
373
382
  type PageMeta,
374
383
  type NavFromPagesOptions,
375
384
  } from './pages';
385
+ import { resolveClayClientDir } from './client-dir';
386
+
387
+ export { resolveClayClientDir } from './client-dir';
376
388
 
377
389
  export type {
390
+ ButtonProps,
391
+ IconButtonProps,
392
+ ButtonVariant,
393
+ ButtonSize,
394
+ LabelProps,
395
+ InputProps,
396
+ CheckboxProps,
397
+ BadgeProps,
398
+ LinkProps,
378
399
  DataTableProps,
379
400
  DataTableAction,
380
401
  DataTableView,
@@ -433,6 +454,7 @@ export type {
433
454
  ChooseOption,
434
455
  AppProps,
435
456
  AppNavItem,
457
+ AppPrimaryAction,
436
458
  AppUser,
437
459
  MarkdownProps,
438
460
  HtmlProps,
@@ -584,6 +606,11 @@ export function button(text?: string, props?: Omit<ButtonProps, 'text'>): Elemen
584
606
  return buttonFactory(text, props);
585
607
  }
586
608
 
609
+ /** Button with a Lucide icon (label optional). Prefer `ui.iconButton`. See {@link IconButtonProps}. */
610
+ export function iconButton(props: IconButtonProps): Element {
611
+ return iconButtonFactory(props);
612
+ }
613
+
587
614
  /** Text input. Prefer `ui.input`. See {@link InputProps}. */
588
615
  export function input(props?: InputProps): Element {
589
616
  return inputFactory(props);
@@ -654,11 +681,44 @@ export function link(text: string, href: string, props?: Omit<LinkProps, 'href'
654
681
  return linkFactory(text, href, props);
655
682
  }
656
683
 
684
+ /** Open `url` in a new tab (`target=_blank`). Prefer `ui.externalLink`. */
685
+ export function externalLink(
686
+ text: string,
687
+ url: string,
688
+ props?: Omit<LinkProps, 'href' | 'text' | 'external'>,
689
+ ): Element {
690
+ return externalLinkFactory(text, url, props);
691
+ }
692
+
657
693
  /** Small status badge. Prefer `ui.badge`. See {@link BadgeProps}. */
658
694
  export function badge(text?: string, props?: Omit<BadgeProps, 'text'>): Element {
659
695
  return badgeFactory(text, props);
660
696
  }
661
697
 
698
+ /**
699
+ * Icon button that copies `content` via `ui.clipboard` and notifies on success.
700
+ * Prefer over `navigator.clipboard` in page code.
701
+ *
702
+ * @example
703
+ * ui.copyButton(orderId);
704
+ * ui.copyButton(orderId, { label: 'Copy id', size: 'sm' });
705
+ */
706
+ export function copyButton(
707
+ content: string,
708
+ props: Omit<IconButtonProps, 'icon' | 'onClick'> & { notifyMessage?: string } = {},
709
+ ): Element {
710
+ const { notifyMessage, label, ...rest } = props;
711
+ return iconButtonFactory({
712
+ ...rest,
713
+ icon: 'copy',
714
+ label,
715
+ onClick: () => {
716
+ clipboardCore(content);
717
+ notifyCore(notifyMessage ?? 'Copied', 'success');
718
+ },
719
+ });
720
+ }
721
+
662
722
  /** Inline alert banner. Prefer `ui.alert`. See {@link AlertProps}. */
663
723
  export function alert(message?: string, props?: Omit<AlertProps, 'message'>): Element {
664
724
  return alertFactory(message, props);
@@ -679,7 +739,7 @@ export function separator(props?: SeparatorProps): Element {
679
739
  return separatorFactory(props);
680
740
  }
681
741
 
682
- /** Lucide icon by name. Prefer `ui.icon`. See {@link IconProps}. */
742
+ /** Lucide icon by kebab-case name (full set in client). Prefer `ui.icon`. See {@link IconProps}. */
683
743
  export function icon(name: string, props?: Omit<IconProps, 'name'>): Element {
684
744
  return iconFactory(name, props);
685
745
  }
@@ -1177,6 +1237,21 @@ export function clipboard(content: string): void {
1177
1237
  clipboardCore(content);
1178
1238
  }
1179
1239
 
1240
+ /** URL hash without `#` (from hello / last `setUrlHash`). Prefer over `window.location`. */
1241
+ export function getUrlHash(): string {
1242
+ return getUrlHashCore();
1243
+ }
1244
+
1245
+ /** Set the browser URL hash. Prefer over `window.history` / `location.hash`. */
1246
+ export function setUrlHash(hash: string): void {
1247
+ setUrlHashCore(hash);
1248
+ }
1249
+
1250
+ /** Open a URL in a new tab. Prefer over `runJavaScript('window.open…')`. */
1251
+ export function openExternal(url: string): void {
1252
+ openExternalCore(url);
1253
+ }
1254
+
1180
1255
  /** Run trusted JavaScript in the connected browser. */
1181
1256
  export function runJavaScript(code: string): void {
1182
1257
  runJavaScriptCore(code);
@@ -1295,6 +1370,10 @@ export function run(rootOrConfig: PageFn | RunConfig = {}, config: RunConfig = {
1295
1370
  corePage('/', root);
1296
1371
  }
1297
1372
 
1373
+ if (!cfg.clientDir) {
1374
+ cfg = { ...cfg, clientDir: resolveClayClientDir() };
1375
+ }
1376
+
1298
1377
  const { app: appProps, ...serverConfig } = cfg;
1299
1378
  if (appProps) {
1300
1379
  setPageWrapper((pageFn) => app(appProps, pageFn));
@@ -1311,6 +1390,7 @@ export function run(rootOrConfig: PageFn | RunConfig = {}, config: RunConfig = {
1311
1390
  export const ui = {
1312
1391
  label,
1313
1392
  button,
1393
+ iconButton,
1314
1394
  input,
1315
1395
  checkbox,
1316
1396
  switch: switch_,
@@ -1324,6 +1404,8 @@ export const ui = {
1324
1404
  slider,
1325
1405
  textArea,
1326
1406
  link,
1407
+ externalLink,
1408
+ copyButton,
1327
1409
  badge,
1328
1410
  alert,
1329
1411
  spinner,
@@ -1399,6 +1481,9 @@ export const ui = {
1399
1481
  clearAuthSession,
1400
1482
  download,
1401
1483
  clipboard,
1484
+ getUrlHash,
1485
+ setUrlHash,
1486
+ openExternal,
1402
1487
  runJavaScript,
1403
1488
  scroll,
1404
1489
  timer,