@flxgde/gigamenu 0.2.0 → 0.5.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 CHANGED
@@ -1,16 +1,24 @@
1
1
  # Gigamenu
2
2
 
3
- A keyboard-driven command palette menu for Angular applications. Inspired by VS Code's Command Palette, Spotlight, and Linear's command menu.
3
+ A keyboard-driven command palette menu for Angular applications. Inspired by VS Code's Command Palette, Spotlight and zsh-completion.
4
4
 
5
5
  ## Features
6
6
 
7
- - Keyboard shortcuts: `Ctrl/Cmd+K` and `/` (when no input is focused)
8
- - Auto-discovery of routes from Angular Router
9
- - Manual command registration API
10
- - Full keyboard navigation (arrow keys, Enter, Escape)
11
- - Fuzzy search filtering
12
- - Dark mode support
13
- - Tailwind CSS styling
7
+ - **Keyboard shortcuts**: `Ctrl/Cmd+K` and `/` (when no input is focused)
8
+ - **Auto-discovery**: Routes from Angular Router with filtering and mapping
9
+ - **Command registration**: With parameters, keywords, and icon support
10
+ - **Step-by-step input**: Select action first, then fill parameters one at a time
11
+ - **Keyboard navigation**: Arrow keys, Enter, Escape, Tab for step progression
12
+ - **Zsh-like editing**: `Ctrl+W` delete word, `Ctrl+U` clear line, and more
13
+ - **Smart search**: Multi-word fuzzy filtering with keyword matching
14
+ - **Frecency ranking**: Learns from your usage patterns
15
+ - **Parameter autocomplete**: Suggestions shown in main list during parameter input
16
+ - **Breadcrumb navigation**: Visual trail of locked action + filled parameters
17
+ - **Custom templates**: 5 template directives for full UI customization
18
+ - **Dark mode**: Configurable class name support
19
+ - **Icon libraries**: Support for emoji and CSS icon classes (FontAwesome, PrimeIcons, etc.)
20
+ - **Type-safe**: Full TypeScript support with helper functions
21
+ - **Tailwind CSS**: Beautiful default styling with dark mode variants
14
22
 
15
23
  ## Installation
16
24
 
@@ -53,7 +61,7 @@ this.gigamenu.registerCommand({
53
61
  description: 'Switch between light and dark theme',
54
62
  icon: '🌙',
55
63
  keywords: ['theme', 'dark', 'light'],
56
- action: () => document.documentElement.classList.toggle('dark'),
64
+ action: () => this.gigamenu.toggleDarkMode(),
57
65
  });
58
66
  ```
59
67
 
@@ -68,16 +76,128 @@ this.gigamenu.registerPage({
68
76
  });
69
77
  ```
70
78
 
79
+ ### 4. Commands with Parameters
80
+
81
+ Commands can define parameters that are filled in step-by-step:
82
+
83
+ ```typescript
84
+ this.gigamenu.registerCommand({
85
+ id: 'cmd:send-message',
86
+ label: 'Send Message',
87
+ description: 'Send a message to a user',
88
+ icon: '💬',
89
+ keywords: ['message', 'chat', 'notify'],
90
+ params: ['user', 'message'], // Parameters filled one at a time
91
+ paramProviders: {
92
+ user: [
93
+ { label: 'Alice', value: 'user-1' },
94
+ { label: 'Bob', value: 'user-2' },
95
+ ],
96
+ },
97
+ action: (args) => {
98
+ // args contains space-separated parameter values
99
+ console.log('Sending:', args);
100
+ },
101
+ });
102
+ ```
103
+
104
+ **Step-by-step flow:**
105
+ 1. User searches "Send Message" and presses Tab/Enter
106
+ 2. Action is locked, input clears for "user" parameter
107
+ 3. Autocomplete suggestions appear in the list
108
+ 4. User selects or types a value, presses Tab
109
+ 5. Input clears for "message" parameter
110
+ 6. User types message, presses Enter to execute
111
+
112
+ ### 5. Route Discovery with Filtering and Mapping
113
+
114
+ Customize which routes are discovered and how they appear:
115
+
116
+ ```typescript
117
+ this.gigamenu.discoverRoutes({
118
+ filter: (route) => {
119
+ // Exclude admin routes
120
+ return !route.fullPath.includes('admin');
121
+ },
122
+ map: (route) => {
123
+ // Customize page data
124
+ return {
125
+ icon: route.data?.['icon'],
126
+ keywords: route.data?.['keywords'],
127
+ description: route.data?.['description'],
128
+ };
129
+ },
130
+ });
131
+ ```
132
+
133
+ ### 6. Custom Templates
134
+
135
+ Customize the appearance of menu items, empty states, header, footer, or the entire panel:
136
+
137
+ ```html
138
+ <gm-gigamenu>
139
+ <!-- Custom item template -->
140
+ <ng-template gmItem let-item let-selected="selected">
141
+ <div [class.selected]="selected">
142
+ <span>{{ item.icon }}</span>
143
+ <strong>{{ item.label }}</strong>
144
+ <em>{{ item.description }}</em>
145
+ </div>
146
+ </ng-template>
147
+
148
+ <!-- Custom empty state -->
149
+ <ng-template gmEmpty let-query>
150
+ <p>No results for "{{ query }}"</p>
151
+ </ng-template>
152
+
153
+ <!-- Custom header with breadcrumb support -->
154
+ <ng-template gmHeader let-query
155
+ let-lockedAction="lockedAction"
156
+ let-paramValues="paramValues"
157
+ let-currentParamName="currentParamName"
158
+ let-onQueryChange="onQueryChange"
159
+ let-onKeydown="onKeydown"
160
+ let-onUnlockAction="onUnlockAction"
161
+ let-placeholder="placeholder">
162
+ <!-- Show breadcrumb when action is locked -->
163
+ @if (lockedAction) {
164
+ <div class="breadcrumb">
165
+ <button (click)="onUnlockAction()">{{ lockedAction.label }}</button>
166
+ @for (value of paramValues; track $index) {
167
+ <span>›</span>
168
+ <span>{{ lockedAction.params?.[$index] }}: {{ value }}</span>
169
+ }
170
+ @if (currentParamName) {
171
+ <span>› {{ currentParamName }}:</span>
172
+ }
173
+ </div>
174
+ }
175
+ <input
176
+ [value]="query"
177
+ [placeholder]="lockedAction ? currentParamName : placeholder"
178
+ (input)="onQueryChange($any($event.target).value)"
179
+ (keydown)="onKeydown($event)" />
180
+ </ng-template>
181
+
182
+ <!-- Custom footer -->
183
+ <ng-template gmFooter let-count let-total="total">
184
+ <p>Showing {{ count }} of {{ total }} items</p>
185
+ </ng-template>
186
+ </gm-gigamenu>
187
+ ```
188
+
71
189
  ## API
72
190
 
73
191
  ### GigamenuService
74
192
 
75
193
  | Method | Description |
76
194
  |--------|-------------|
195
+ | `setRouter(router)` | Set the router instance (required for navigation) |
77
196
  | `open()` | Open the menu |
78
197
  | `close()` | Close the menu |
79
198
  | `toggle()` | Toggle menu visibility |
80
- | `discoverRoutes(routes?)` | Auto-discover pages from Angular Router |
199
+ | `toggleDarkMode()` | Toggle dark mode using configured class |
200
+ | `discoverRoutes(options?)` | Auto-discover pages from Angular Router with optional filter/map |
81
201
  | `registerCommand(command)` | Register a custom command |
82
202
  | `registerPage(page)` | Register a custom page |
83
203
  | `registerItem(item)` | Register a generic menu item |
@@ -91,6 +211,8 @@ interface GigamenuConfig {
91
211
  placeholder?: string; // Search input placeholder
92
212
  maxResults?: number; // Maximum items to show (default: 10)
93
213
  autoDiscoverRoutes?: boolean; // Auto-discover on init
214
+ argSeparator?: string; // Separator between args when passed to action
215
+ darkModeClass?: string; // CSS class for dark mode (default: 'dark')
94
216
  }
95
217
  ```
96
218
 
@@ -101,26 +223,193 @@ interface GigamenuItem {
101
223
  id: string;
102
224
  label: string;
103
225
  description?: string;
226
+ icon?: string; // Emoji or text icon
227
+ iconClass?: string; // CSS class for icon libraries (e.g., 'pi pi-home', 'fa fa-user')
228
+ keywords?: string[]; // Additional searchable keywords
229
+ params?: string[]; // Required parameter names (e.g., ['id', 'commentId'])
230
+ category: 'page' | 'command';
231
+ action: (args?: string) => void; // Receives arguments after separator
232
+ }
233
+
234
+ interface GigamenuCommand extends Omit<GigamenuItem, 'category'> {
235
+ shortcut?: string; // Keyboard shortcut display (e.g., 'Ctrl+S')
236
+ }
237
+
238
+ interface GigamenuPage extends Omit<GigamenuItem, 'category' | 'action'> {
239
+ path: string; // Navigation path (params auto-extracted)
240
+ }
241
+
242
+ interface DiscoverRoutesOptions {
243
+ filter?: (route: RouteInfo) => boolean; // Filter which routes to include
244
+ map?: (route: RouteInfo) => MappedPage | null; // Customize page data
245
+ }
246
+
247
+ interface RouteInfo {
248
+ path: string; // Segment path
249
+ fullPath: string; // Complete path from root
250
+ data?: Record<string, unknown>; // Route data
251
+ title?: string; // Route title
252
+ }
253
+
254
+ interface MappedPage {
255
+ label?: string;
256
+ description?: string;
104
257
  icon?: string;
258
+ iconClass?: string;
105
259
  keywords?: string[];
106
- category: 'page' | 'command';
107
- action: () => void;
108
260
  }
261
+
262
+ // Type-safe command definitions
263
+ interface CommandDefinition {
264
+ readonly id: string;
265
+ readonly label: string;
266
+ readonly description?: string;
267
+ readonly icon?: string;
268
+ readonly iconClass?: string;
269
+ readonly keywords?: string[];
270
+ readonly shortcut?: string;
271
+ execute(): void;
272
+ }
273
+
274
+ // Helper for type-safe command creation
275
+ function defineCommand(command: CommandDefinition): CommandDefinition
109
276
  ```
110
277
 
278
+ ### Template Directives
279
+
280
+ All template directives provide context objects for customization:
281
+
282
+ | Directive | Purpose | Context Properties |
283
+ |-----------|---------|-------------------|
284
+ | `gmItem` | Custom item rendering | `item`, `index`, `selected` |
285
+ | `gmEmpty` | Empty state when no results | `query` (search term) |
286
+ | `gmHeader` | Search input and header area | `query`, `lockedAction`, `paramValues`, `currentParamName`, `placeholder`, `onQueryChange`, `onKeydown`, `onUnlockAction`, `onGoToParam` |
287
+ | `gmFooter` | Footer/status area | `count` (filtered), `total` (all items) |
288
+ | `gmPanel` | Entire panel container | `items`, `query`, `lockedAction`, `paramValues`, `selectedIndex`, `placeholder`, `onItemClick`, `onSelectIndex`, `onQueryChange`, `onClose` |
289
+
290
+ ## Advanced Features
291
+
292
+ ### Step-by-Step Input Flow
293
+
294
+ Gigamenu uses a step-by-step approach for commands with parameters:
295
+
296
+ **State Machine:**
297
+ 1. **ActionSelection**: Search and select an action from the list
298
+ 2. **ParameterInput**: Fill in parameters one at a time (when action has params)
299
+
300
+ **Behavior by Action Type:**
301
+ - **No params**: Tab or Enter executes immediately
302
+ - **Required params**: Tab or Enter locks action, enters parameter input mode
303
+ - **Optional params**: Enter executes, Tab enters parameter input mode
304
+
305
+ **Parameter Input Mode:**
306
+ - Input field only captures the current parameter value
307
+ - Main list shows autocomplete suggestions (if `paramProviders` defined)
308
+ - Breadcrumb shows: `[Action] › [param1: value] › [param2: value] › current:`
309
+ - Navigate suggestions with arrow keys, select with Tab or Enter
310
+
311
+ **Navigation:**
312
+ - Backspace (on empty) or Escape: Go back to previous parameter
313
+ - At first parameter: Go back to action selection
314
+ - Click breadcrumb items to jump back to any step
315
+
316
+ ### Frecency-Based Ranking
317
+
318
+ Gigamenu uses intelligent ranking based on **frequency** and **recency** of selections:
319
+
320
+ - Learns from your usage patterns
321
+ - Recent selections are weighted higher
322
+ - Automatically selects frequently used items
323
+ - Decays scores over time (formula: `count × 0.9^ageInHours`)
324
+ - Persists to localStorage for cross-session learning
325
+ - Auto-prunes low-scoring entries (max 100 terms, 10 items per term)
326
+
327
+ No configuration needed - it works automatically!
328
+
329
+ ### Parameter Color Coding
330
+
331
+ Filled parameter values in the breadcrumb are color-coded:
332
+
333
+ ```
334
+ [Send Message] › [user: Alice] › [message: Hello]
335
+ ↑ blue ↑ green
336
+ ```
337
+
338
+ 5 colors cycle for multiple parameters: blue, green, orange, pink, cyan (with dark mode variants).
339
+
111
340
  ## Keyboard Shortcuts
112
341
 
342
+ ### Action Selection Mode
113
343
  | Shortcut | Action |
114
344
  |----------|--------|
115
- | `Ctrl/Cmd+K` | Open menu |
345
+ | `Ctrl/Cmd+K` | Open/toggle menu |
116
346
  | `/` | Open menu (when no input focused) |
117
347
  | `↑` / `↓` | Navigate items |
118
- | `Enter` | Execute selected item |
348
+ | `Enter` | Execute (or enter param mode if required params) |
349
+ | `Tab` | Enter param mode (or execute if no params) |
119
350
  | `Escape` | Close menu |
120
351
 
352
+ ### Parameter Input Mode
353
+ | Shortcut | Action |
354
+ |----------|--------|
355
+ | `↑` / `↓` | Navigate autocomplete suggestions |
356
+ | `Tab` | Accept suggestion + next param (or execute if last) |
357
+ | `Enter` | Accept suggestion + next param (or execute if last) |
358
+ | `Backspace` (empty) | Go back to previous param |
359
+ | `Escape` | Go back to previous param (or action selection) |
360
+
361
+ ### Zsh-like Editing
362
+ | Shortcut | Action |
363
+ |----------|--------|
364
+ | `Ctrl+W` | Delete last word |
365
+ | `Ctrl+U` | Clear entire line |
366
+ | `Ctrl+Backspace` | Delete last word |
367
+ | `Alt+Backspace` | Delete last word |
368
+
121
369
  ## Styling
122
370
 
123
- Gigamenu uses Tailwind CSS and supports dark mode via the `dark` class on `<html>`.
371
+ Gigamenu uses Tailwind CSS and supports dark mode. By default, it uses the `dark` class on `<html>`, but this is customizable via the `darkModeClass` configuration option:
372
+
373
+ ```typescript
374
+ this.gigamenu.configure({
375
+ darkModeClass: 'dark-theme', // Use your custom class name
376
+ });
377
+ ```
378
+
379
+ ## Architecture
380
+
381
+ For contributors and maintainers, here's an overview of the codebase structure:
382
+
383
+ ### File Structure
384
+
385
+ ```
386
+ src/lib/
387
+ ├── gigamenu.component.ts # Main component (~520 lines, orchestrator)
388
+ ├── gigamenu.component.html # Template
389
+ ├── gigamenu.service.ts # State management service
390
+ ├── frecency.service.ts # Usage-based ranking
391
+ ├── types.ts # Type definitions
392
+ ├── query-parser.ts # Query/args parsing
393
+ ├── input-state.ts # State machine enum
394
+ ├── gigamenu-templates.directive.ts # Template directives
395
+ └── core/ # Pure functions (business logic)
396
+ ├── index.ts # Barrel export
397
+ ├── scroll-utils.ts # DOM scroll helpers
398
+ ├── search.ts # Filtering & sorting
399
+ ├── parameter-state.ts # Parameter computations
400
+ ├── template-contexts.ts # Template context builders
401
+ ├── menu-lifecycle.ts # Menu state utilities
402
+ ├── autocomplete.ts # Autocomplete logic
403
+ └── keyboard-handlers.ts # Keyboard handlers + actions
404
+ ```
405
+
406
+ ### Design Principles
407
+
408
+ - **Separation of concerns**: Business logic lives in `core/` as pure, testable functions
409
+ - **Action dispatch pattern**: Keyboard handlers return `MenuAction[]` objects instead of mutating state
410
+ - **State machine**: `InputState` enum (Closed, ActionSelection, ParameterInput) drives behavior
411
+ - **Step-by-step flow**: Input parses one thing at a time (action search OR parameter value)
412
+ - **Angular signals**: All reactive state uses `signal()` and `computed()`
124
413
 
125
414
  ## License
126
415