@tooee/shell 0.1.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.
Files changed (57) hide show
  1. package/README.md +5 -0
  2. package/dist/CommandPaletteOverlay.d.ts +7 -0
  3. package/dist/CommandPaletteOverlay.d.ts.map +1 -0
  4. package/dist/CommandPaletteOverlay.js +29 -0
  5. package/dist/CommandPaletteOverlay.js.map +1 -0
  6. package/dist/ThemePickerOverlay.d.ts +6 -0
  7. package/dist/ThemePickerOverlay.d.ts.map +1 -0
  8. package/dist/ThemePickerOverlay.js +20 -0
  9. package/dist/ThemePickerOverlay.js.map +1 -0
  10. package/dist/command-palette.d.ts +9 -0
  11. package/dist/command-palette.d.ts.map +1 -0
  12. package/dist/command-palette.js +51 -0
  13. package/dist/command-palette.js.map +1 -0
  14. package/dist/commands.d.ts +18 -0
  15. package/dist/commands.d.ts.map +1 -0
  16. package/dist/commands.js +49 -0
  17. package/dist/commands.js.map +1 -0
  18. package/dist/index.d.ts +12 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +9 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/launch.d.ts +6 -0
  23. package/dist/launch.d.ts.map +1 -0
  24. package/dist/launch.js +12 -0
  25. package/dist/launch.js.map +1 -0
  26. package/dist/modal.d.ts +34 -0
  27. package/dist/modal.d.ts.map +1 -0
  28. package/dist/modal.js +440 -0
  29. package/dist/modal.js.map +1 -0
  30. package/dist/overlay.d.ts +11 -0
  31. package/dist/overlay.d.ts.map +1 -0
  32. package/dist/overlay.js +145 -0
  33. package/dist/overlay.js.map +1 -0
  34. package/dist/provider.d.ts +11 -0
  35. package/dist/provider.d.ts.map +1 -0
  36. package/dist/provider.js +13 -0
  37. package/dist/provider.js.map +1 -0
  38. package/dist/search.d.ts +2 -0
  39. package/dist/search.d.ts.map +1 -0
  40. package/dist/search.js +20 -0
  41. package/dist/search.js.map +1 -0
  42. package/dist/theme-picker.d.ts +16 -0
  43. package/dist/theme-picker.d.ts.map +1 -0
  44. package/dist/theme-picker.js +48 -0
  45. package/dist/theme-picker.js.map +1 -0
  46. package/package.json +49 -0
  47. package/src/CommandPaletteOverlay.tsx +44 -0
  48. package/src/ThemePickerOverlay.tsx +28 -0
  49. package/src/command-palette.ts +73 -0
  50. package/src/commands.ts +60 -0
  51. package/src/index.ts +11 -0
  52. package/src/launch.tsx +15 -0
  53. package/src/modal.ts +504 -0
  54. package/src/overlay.tsx +220 -0
  55. package/src/provider.tsx +46 -0
  56. package/src/search.ts +18 -0
  57. package/src/theme-picker.ts +84 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAoB,MAAM,eAAe,CAAA;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,eAAe,EAAa,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAS/C,MAAM,UAAU,aAAa,CAAC,EAC5B,QAAQ,EACR,MAAM,EACN,MAAM,EAAE,eAAe,EACvB,WAAW,GACQ;IACnB,OAAO,CACL,KAAC,cAAc,IAAC,SAAS,EAAE,eAAe,YACxC,KAAC,kBAAkB,IAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,YACzD,QAAQ,GACU,GACN,CAClB,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,EAC1B,QAAQ,EACR,MAAM,EACN,WAAW,GAKZ;IACC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,OAAO,CACL,KAAC,qBAAqB,IAAC,YAAY,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,KAAK,EAAE,IAAI,YACtF,KAAC,eAAe,IAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,YAC5E,KAAC,eAAe,cAAE,QAAQ,GAAmB,GAC7B,GACI,CACzB,CAAA;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function findMatchingLines(text: string, query: string): number[];
2
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBvE"}
package/dist/search.js ADDED
@@ -0,0 +1,20 @@
1
+ export function findMatchingLines(text, query) {
2
+ if (!query)
3
+ return [];
4
+ const lowerQuery = query.toLowerCase();
5
+ const results = [];
6
+ let lineStart = 0;
7
+ let lineNum = 0;
8
+ for (let i = 0; i <= text.length; i++) {
9
+ if (i === text.length || text[i] === "\n") {
10
+ const line = text.slice(lineStart, i).toLowerCase();
11
+ if (line.includes(lowerQuery)) {
12
+ results.push(lineNum);
13
+ }
14
+ lineStart = i + 1;
15
+ lineNum++;
16
+ }
17
+ }
18
+ return results;
19
+ }
20
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,KAAa;IAC3D,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAA;IACtC,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA;YACnD,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC9B,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACvB,CAAC;YACD,SAAS,GAAG,CAAC,GAAG,CAAC,CAAA;YACjB,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface ThemePickerEntry {
2
+ id: string;
3
+ title: string;
4
+ }
5
+ export interface ThemePickerState {
6
+ isOpen: boolean;
7
+ open: () => void;
8
+ close: () => void;
9
+ confirm: (name: string) => void;
10
+ preview: (name: string) => void;
11
+ entries: ThemePickerEntry[];
12
+ originalTheme: string;
13
+ currentTheme: string;
14
+ }
15
+ export declare function useThemePicker(): ThemePickerState;
16
+ //# sourceMappingURL=theme-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-picker.d.ts","sourceRoot":"","sources":["../src/theme-picker.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;CACrB;AAID,wBAAgB,cAAc,IAAI,gBAAgB,CA0DjD"}
@@ -0,0 +1,48 @@
1
+ import { useCallback, useRef, useState } from "react";
2
+ import { createElement } from "react";
3
+ import { useThemeSwitcher } from "@tooee/themes";
4
+ import { useOverlay } from "@tooee/overlays";
5
+ import { ThemePickerOverlay } from "./ThemePickerOverlay.jsx";
6
+ const OVERLAY_ID = "theme-picker";
7
+ export function useThemePicker() {
8
+ const { allThemes, setTheme, name: currentTheme } = useThemeSwitcher();
9
+ const overlay = useOverlay();
10
+ const [isOpen, setIsOpen] = useState(false);
11
+ const originalThemeRef = useRef(currentTheme);
12
+ const entries = allThemes.map((name) => ({
13
+ id: name,
14
+ title: name,
15
+ }));
16
+ const close = useCallback(() => {
17
+ setTheme(originalThemeRef.current);
18
+ setIsOpen(false);
19
+ overlay.hide(OVERLAY_ID);
20
+ }, [setTheme, overlay]);
21
+ const confirm = useCallback((name) => {
22
+ setTheme(name);
23
+ setIsOpen(false);
24
+ overlay.hide(OVERLAY_ID);
25
+ }, [setTheme, overlay]);
26
+ const preview = useCallback((name) => {
27
+ setTheme(name);
28
+ }, [setTheme]);
29
+ const open = useCallback(() => {
30
+ originalThemeRef.current = currentTheme;
31
+ setIsOpen(true);
32
+ overlay.open(OVERLAY_ID, ({ close }) => createElement(ThemePickerOverlay, {
33
+ originalTheme: currentTheme,
34
+ close: () => close(),
35
+ }), null, { mode: "insert", dismissOnEscape: true, onClose: () => setIsOpen(false) });
36
+ }, [overlay, currentTheme]);
37
+ return {
38
+ isOpen,
39
+ open,
40
+ close,
41
+ confirm,
42
+ preview,
43
+ entries,
44
+ originalTheme: originalThemeRef.current,
45
+ currentTheme,
46
+ };
47
+ }
48
+ //# sourceMappingURL=theme-picker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-picker.js","sourceRoot":"","sources":["../src/theme-picker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAkB7D,MAAM,UAAU,GAAG,cAAc,CAAA;AAEjC,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,gBAAgB,EAAE,CAAA;IACtE,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;IAC5B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,gBAAgB,GAAG,MAAM,CAAS,YAAY,CAAC,CAAA;IAErD,MAAM,OAAO,GAAuB,SAAS,CAAC,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC;QACnE,EAAE,EAAE,IAAI;QACR,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC,CAAA;IAEH,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,QAAQ,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAA;QAClC,SAAS,CAAC,KAAK,CAAC,CAAA;QAChB,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAC1B,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAA;IAEvB,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,IAAY,EAAE,EAAE;QACf,QAAQ,CAAC,IAAI,CAAC,CAAA;QACd,SAAS,CAAC,KAAK,CAAC,CAAA;QAChB,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAC1B,CAAC,EACD,CAAC,QAAQ,EAAE,OAAO,CAAC,CACpB,CAAA;IAED,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,IAAY,EAAE,EAAE;QACf,QAAQ,CAAC,IAAI,CAAC,CAAA;IAChB,CAAC,EACD,CAAC,QAAQ,CAAC,CACX,CAAA;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5B,gBAAgB,CAAC,OAAO,GAAG,YAAY,CAAA;QACvC,SAAS,CAAC,IAAI,CAAC,CAAA;QACf,OAAO,CAAC,IAAI,CACV,UAAU,EACV,CAAC,EAAE,KAAK,EAAoD,EAAE,EAAE,CAC9D,aAAa,CAAC,kBAAkB,EAAE;YAChC,aAAa,EAAE,YAAY;YAC3B,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE;SACrB,CAAC,EACJ,IAAI,EACJ,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAC3E,CAAA;IACH,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAA;IAE3B,OAAO;QACL,MAAM;QACN,IAAI;QACJ,KAAK;QACL,OAAO;QACP,OAAO;QACP,OAAO;QACP,aAAa,EAAE,gBAAgB,CAAC,OAAO;QACvC,YAAY;KACb,CAAA;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@tooee/shell",
3
+ "version": "0.1.0",
4
+ "description": "Composition layer wiring Tooee apps together",
5
+ "license": "MIT",
6
+ "author": "Gareth Andrew",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/gingerhendrix/tooee.git",
10
+ "directory": "packages/shell"
11
+ },
12
+ "homepage": "https://github.com/gingerhendrix/tooee",
13
+ "bugs": "https://github.com/gingerhendrix/tooee/issues",
14
+ "keywords": ["tui", "terminal", "cli", "opentui"],
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "import": {
19
+ "@tooee/source": "./src/index.ts",
20
+ "default": "./dist/index.js"
21
+ }
22
+ }
23
+ },
24
+ "files": ["dist", "src"],
25
+ "scripts": {
26
+ "typecheck": "tsc --noEmit"
27
+ },
28
+ "dependencies": {
29
+ "@tooee/commands": "0.0.0",
30
+ "@tooee/config": "0.0.0",
31
+ "@tooee/clipboard": "0.0.0",
32
+ "@tooee/layout": "0.0.0",
33
+ "@tooee/overlays": "0.0.0",
34
+ "@tooee/renderers": "0.0.0",
35
+ "@tooee/themes": "0.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@opentui/core": "^0.1.67",
39
+ "@opentui/react": "^0.1.67",
40
+ "@types/bun": "^1.3.5",
41
+ "@types/react": "^19.1.10",
42
+ "typescript": "^5.8.3"
43
+ },
44
+ "peerDependencies": {
45
+ "@opentui/core": "^0.1.67",
46
+ "@opentui/react": "^0.1.67",
47
+ "react": "^18.0.0 || ^19.0.0"
48
+ }
49
+ }
@@ -0,0 +1,44 @@
1
+ import { createElement, useMemo } from "react"
2
+ import type { ReactNode } from "react"
3
+ import { useCommandContext } from "@tooee/commands"
4
+ import type { Mode } from "@tooee/commands"
5
+ import { CommandPalette } from "@tooee/renderers"
6
+
7
+ const DEFAULT_MODES: Mode[] = ["cursor"]
8
+
9
+ export function CommandPaletteOverlay({
10
+ launchMode,
11
+ close,
12
+ }: {
13
+ launchMode: Mode
14
+ close: () => void
15
+ }): ReactNode {
16
+ const { commands, invoke } = useCommandContext()
17
+
18
+ const entries = useMemo(
19
+ () =>
20
+ commands
21
+ .filter((cmd) => !cmd.hidden)
22
+ .filter((cmd) => {
23
+ const cmdModes = cmd.modes ?? DEFAULT_MODES
24
+ return cmdModes.includes(launchMode)
25
+ })
26
+ .map((cmd) => ({
27
+ id: cmd.id,
28
+ title: cmd.title,
29
+ hotkey: cmd.defaultHotkey,
30
+ category: cmd.category,
31
+ icon: cmd.icon,
32
+ })),
33
+ [commands, launchMode],
34
+ )
35
+
36
+ return createElement(CommandPalette, {
37
+ commands: entries,
38
+ onSelect: (id: string) => {
39
+ close()
40
+ invoke(id)
41
+ },
42
+ onClose: close,
43
+ })
44
+ }
@@ -0,0 +1,28 @@
1
+ import { createElement } from "react"
2
+ import type { ReactNode } from "react"
3
+ import { useThemeSwitcher, ThemePicker } from "@tooee/themes"
4
+
5
+ export function ThemePickerOverlay({
6
+ originalTheme,
7
+ close,
8
+ }: {
9
+ originalTheme: string
10
+ close: () => void
11
+ }): ReactNode {
12
+ const { allThemes, name: currentTheme, setTheme } = useThemeSwitcher()
13
+ const entries = allThemes.map((name: string) => ({ id: name, title: name }))
14
+
15
+ return createElement(ThemePicker, {
16
+ entries,
17
+ currentTheme,
18
+ onNavigate: setTheme,
19
+ onSelect: (name: string) => {
20
+ setTheme(name)
21
+ close()
22
+ },
23
+ onClose: () => {
24
+ setTheme(originalTheme)
25
+ close()
26
+ },
27
+ })
28
+ }
@@ -0,0 +1,73 @@
1
+ import { useCallback, useMemo, useRef, useState } from "react"
2
+ import { createElement } from "react"
3
+ import { useCommandContext, useCommand, useMode } from "@tooee/commands"
4
+ import type { Mode } from "@tooee/commands"
5
+ import { useOverlay } from "@tooee/overlays"
6
+ import type { OverlayCloseReason } from "@tooee/overlays"
7
+ import type { CommandPaletteEntry } from "@tooee/renderers"
8
+ import { CommandPaletteOverlay } from "./CommandPaletteOverlay.jsx"
9
+
10
+ const DEFAULT_MODES: Mode[] = ["cursor"]
11
+ const OVERLAY_ID = "command-palette"
12
+
13
+ export interface CommandPaletteState {
14
+ isOpen: boolean
15
+ open: () => void
16
+ close: () => void
17
+ entries: CommandPaletteEntry[]
18
+ }
19
+
20
+ export function useCommandPalette(): CommandPaletteState {
21
+ const { commands } = useCommandContext()
22
+ const mode = useMode()
23
+ const overlay = useOverlay()
24
+ const [isOpen, setIsOpen] = useState(false)
25
+ const launchModeRef = useRef<Mode>(mode)
26
+
27
+ const launchMode = launchModeRef.current
28
+ const entries = useMemo(() => {
29
+ return commands
30
+ .filter((cmd) => !cmd.hidden)
31
+ .filter((cmd) => {
32
+ const cmdModes = cmd.modes ?? DEFAULT_MODES
33
+ return cmdModes.includes(launchMode)
34
+ })
35
+ .map((cmd) => ({
36
+ id: cmd.id,
37
+ title: cmd.title,
38
+ hotkey: cmd.defaultHotkey,
39
+ category: cmd.category,
40
+ icon: cmd.icon,
41
+ }))
42
+ }, [commands, launchMode])
43
+
44
+ const close = useCallback(() => {
45
+ setIsOpen(false)
46
+ overlay.hide(OVERLAY_ID)
47
+ }, [overlay])
48
+
49
+ const open = useCallback(() => {
50
+ launchModeRef.current = mode
51
+ setIsOpen(true)
52
+ overlay.open(
53
+ OVERLAY_ID,
54
+ ({ close }: { close: (reason?: OverlayCloseReason) => void }) =>
55
+ createElement(CommandPaletteOverlay, {
56
+ launchMode: mode,
57
+ close: () => close(),
58
+ }),
59
+ null,
60
+ { mode: "insert", dismissOnEscape: true, onClose: () => setIsOpen(false) },
61
+ )
62
+ }, [overlay, mode])
63
+
64
+ useCommand({
65
+ id: "command-palette",
66
+ title: "Command Palette",
67
+ hotkey: ":",
68
+ modes: ["cursor"],
69
+ handler: open,
70
+ })
71
+
72
+ return { isOpen, open, close, entries }
73
+ }
@@ -0,0 +1,60 @@
1
+ import { useRenderer } from "@opentui/react"
2
+ import { copyToClipboard } from "@tooee/clipboard"
3
+ import { useCommand, type CommandWhen } from "@tooee/commands"
4
+ import { useThemePicker, type ThemePickerState } from "./theme-picker.js"
5
+
6
+ export function useThemeCommands(opts?: { when?: CommandWhen }): {
7
+ name: string
8
+ picker: ThemePickerState
9
+ } {
10
+ const picker = useThemePicker()
11
+
12
+ useCommand({
13
+ id: "cycle-theme",
14
+ title: "Choose theme",
15
+ hotkey: "t",
16
+ when: opts?.when,
17
+ handler: () => {
18
+ picker.open()
19
+ },
20
+ })
21
+
22
+ return { name: picker.currentTheme, picker }
23
+ }
24
+
25
+ export function useQuitCommand(opts?: {
26
+ hotkey?: string
27
+ when?: CommandWhen
28
+ onQuit?: () => void
29
+ }) {
30
+ const renderer = useRenderer()
31
+
32
+ useCommand({
33
+ id: "quit",
34
+ title: "Quit",
35
+ hotkey: opts?.hotkey ?? "q",
36
+ when: opts?.when,
37
+ handler: () => {
38
+ if (opts?.onQuit) {
39
+ opts.onQuit()
40
+ } else {
41
+ renderer.destroy()
42
+ }
43
+ },
44
+ })
45
+ }
46
+
47
+ export function useCopyCommand(opts: { getText: () => string | undefined; when?: CommandWhen }) {
48
+ useCommand({
49
+ id: "copy",
50
+ title: "Copy to clipboard",
51
+ hotkey: "y",
52
+ when: opts.when,
53
+ handler: () => {
54
+ const text = opts.getText()
55
+ if (text) {
56
+ void copyToClipboard(text)
57
+ }
58
+ },
59
+ })
60
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export { useThemeCommands, useQuitCommand, useCopyCommand } from "./commands.js"
2
+ export { useModalNavigationCommands } from "./modal.js"
3
+ export type { ModalNavigationState, ModalNavigationOptions, Position } from "./modal.js"
4
+ export { findMatchingLines } from "./search.js"
5
+ export { TooeeProvider } from "./provider.jsx"
6
+ export { launchCli } from "./launch.jsx"
7
+ export { useCommandPalette } from "./command-palette.js"
8
+ export type { CommandPaletteState } from "./command-palette.js"
9
+ export { useThemePicker } from "./theme-picker.js"
10
+ export type { ThemePickerState, ThemePickerEntry } from "./theme-picker.js"
11
+ export { OverlayProvider } from "./overlay.jsx"
package/src/launch.tsx ADDED
@@ -0,0 +1,15 @@
1
+ import type { ReactNode } from "react"
2
+ import { createCliRenderer } from "@opentui/core"
3
+ import { createRoot } from "@opentui/react"
4
+ import { TooeeProvider } from "./provider.jsx"
5
+
6
+ export async function launchCli(
7
+ node: ReactNode,
8
+ opts?: { useAlternateScreen?: boolean; exitOnCtrlC?: boolean },
9
+ ): Promise<void> {
10
+ const renderer = await createCliRenderer({
11
+ useAlternateScreen: opts?.useAlternateScreen ?? true,
12
+ exitOnCtrlC: opts?.exitOnCtrlC ?? true,
13
+ })
14
+ createRoot(renderer).render(<TooeeProvider>{node}</TooeeProvider>)
15
+ }