@ship-ui/core 0.22.12 → 0.22.15

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 (72) hide show
  1. package/README.md +46 -0
  2. package/assets/mcp/components.json +579 -2
  3. package/bin/src/scanner.zig +9 -18
  4. package/fesm2022/ship-ui-core-sh-form-field-experimental.mjs +17 -2
  5. package/fesm2022/ship-ui-core-sh-form-field-experimental.mjs.map +1 -1
  6. package/fesm2022/ship-ui-core-ship-a11y-keybindings.mjs +433 -0
  7. package/fesm2022/ship-ui-core-ship-a11y-keybindings.mjs.map +1 -0
  8. package/fesm2022/ship-ui-core-ship-accordion.mjs +1 -0
  9. package/fesm2022/ship-ui-core-ship-accordion.mjs.map +1 -1
  10. package/fesm2022/ship-ui-core-ship-alert.mjs +3 -2
  11. package/fesm2022/ship-ui-core-ship-alert.mjs.map +1 -1
  12. package/fesm2022/ship-ui-core-ship-blueprint.mjs +14 -9
  13. package/fesm2022/ship-ui-core-ship-blueprint.mjs.map +1 -1
  14. package/fesm2022/ship-ui-core-ship-checkbox.mjs +16 -14
  15. package/fesm2022/ship-ui-core-ship-checkbox.mjs.map +1 -1
  16. package/fesm2022/ship-ui-core-ship-color-picker.mjs +3 -1
  17. package/fesm2022/ship-ui-core-ship-color-picker.mjs.map +1 -1
  18. package/fesm2022/ship-ui-core-ship-datepicker.mjs +51 -29
  19. package/fesm2022/ship-ui-core-ship-datepicker.mjs.map +1 -1
  20. package/fesm2022/ship-ui-core-ship-dialog.mjs +10 -5
  21. package/fesm2022/ship-ui-core-ship-dialog.mjs.map +1 -1
  22. package/fesm2022/ship-ui-core-ship-divider.mjs +4 -2
  23. package/fesm2022/ship-ui-core-ship-divider.mjs.map +1 -1
  24. package/fesm2022/ship-ui-core-ship-editor.mjs +2673 -0
  25. package/fesm2022/ship-ui-core-ship-editor.mjs.map +1 -0
  26. package/fesm2022/ship-ui-core-ship-icon.mjs +2 -2
  27. package/fesm2022/ship-ui-core-ship-icon.mjs.map +1 -1
  28. package/fesm2022/ship-ui-core-ship-list.mjs +4 -2
  29. package/fesm2022/ship-ui-core-ship-list.mjs.map +1 -1
  30. package/fesm2022/ship-ui-core-ship-menu.mjs +8 -5
  31. package/fesm2022/ship-ui-core-ship-menu.mjs.map +1 -1
  32. package/fesm2022/ship-ui-core-ship-popover.mjs +10 -5
  33. package/fesm2022/ship-ui-core-ship-popover.mjs.map +1 -1
  34. package/fesm2022/ship-ui-core-ship-progress-bar.mjs +5 -1
  35. package/fesm2022/ship-ui-core-ship-progress-bar.mjs.map +1 -1
  36. package/fesm2022/ship-ui-core-ship-radio.mjs +16 -14
  37. package/fesm2022/ship-ui-core-ship-radio.mjs.map +1 -1
  38. package/fesm2022/ship-ui-core-ship-select.mjs +9 -9
  39. package/fesm2022/ship-ui-core-ship-select.mjs.map +1 -1
  40. package/fesm2022/ship-ui-core-ship-sidenav.mjs +2 -2
  41. package/fesm2022/ship-ui-core-ship-sidenav.mjs.map +1 -1
  42. package/fesm2022/ship-ui-core-ship-spinner.mjs +3 -1
  43. package/fesm2022/ship-ui-core-ship-spinner.mjs.map +1 -1
  44. package/fesm2022/ship-ui-core-ship-spotlight.mjs +77 -24
  45. package/fesm2022/ship-ui-core-ship-spotlight.mjs.map +1 -1
  46. package/fesm2022/ship-ui-core-ship-table.mjs +139 -139
  47. package/fesm2022/ship-ui-core-ship-table.mjs.map +1 -1
  48. package/fesm2022/ship-ui-core-ship-theme-toggle.mjs +2 -2
  49. package/fesm2022/ship-ui-core-ship-theme-toggle.mjs.map +1 -1
  50. package/fesm2022/ship-ui-core-ship-toggle-card.mjs +24 -3
  51. package/fesm2022/ship-ui-core-ship-toggle-card.mjs.map +1 -1
  52. package/fesm2022/ship-ui-core-ship-toggle.mjs +16 -14
  53. package/fesm2022/ship-ui-core-ship-toggle.mjs.map +1 -1
  54. package/fesm2022/ship-ui-core-ship-tree.mjs +2 -2
  55. package/fesm2022/ship-ui-core-ship-tree.mjs.map +1 -1
  56. package/fesm2022/ship-ui-core-ship-virtual-scroll.mjs +2 -2
  57. package/fesm2022/ship-ui-core-ship-virtual-scroll.mjs.map +1 -1
  58. package/fesm2022/ship-ui-core.mjs +36 -23
  59. package/fesm2022/ship-ui-core.mjs.map +1 -1
  60. package/package.json +33 -2
  61. package/types/ship-ui-core-sh-form-field-experimental.d.ts +2 -0
  62. package/types/ship-ui-core-ship-a11y-keybindings.d.ts +102 -0
  63. package/types/ship-ui-core-ship-blueprint.d.ts +1 -1
  64. package/types/ship-ui-core-ship-checkbox.d.ts +2 -1
  65. package/types/ship-ui-core-ship-editor.d.ts +168 -0
  66. package/types/ship-ui-core-ship-radio.d.ts +2 -1
  67. package/types/ship-ui-core-ship-spotlight.d.ts +1 -1
  68. package/types/ship-ui-core-ship-table.d.ts +2 -0
  69. package/types/ship-ui-core-ship-toggle-card.d.ts +1 -0
  70. package/types/ship-ui-core-ship-toggle.d.ts +2 -1
  71. package/types/ship-ui-core.d.ts +3 -0
  72. package/bin/ship-fg-scanner +0 -0
@@ -231,6 +231,430 @@
231
231
  ],
232
232
  "examples": []
233
233
  },
234
+ {
235
+ "name": "ShipEditor",
236
+ "selector": "sh-editor",
237
+ "path": "core/projects/ship-ui/ship-editor/ship-editor.ts",
238
+ "description": "### Usage\n\nShipEditor is a premium, config-driven WYSIWYG rich text editor. It allows typing in design view, editing raw source code, and can serialize to `html`, `markdown`, or `json` configuration dynamically.\n\n### Features\n\n- Three output formats: HTML strings, Markdown text, and custom structured block/mark JSON.\n- ControlValueAccessor integration for Reactive and Template-driven forms.\n- Monospace code-view for editing source code directly.\n- Dynamic toolbar selection tracking.\n- Word and character count reporting in the status footer.",
239
+ "keywords": [
240
+ "editor",
241
+ "wysiwyg",
242
+ "richtext",
243
+ "html",
244
+ "markdown",
245
+ "json",
246
+ "input",
247
+ "form"
248
+ ],
249
+ "inputs": [
250
+ {
251
+ "name": "format",
252
+ "type": "'json' | 'html' | 'markdown'",
253
+ "description": "",
254
+ "defaultValue": "'html'"
255
+ },
256
+ {
257
+ "name": "placeholder",
258
+ "type": "string",
259
+ "description": "",
260
+ "defaultValue": "'Type your content here...'"
261
+ },
262
+ {
263
+ "name": "readonly",
264
+ "type": "boolean",
265
+ "description": "",
266
+ "defaultValue": "false"
267
+ },
268
+ {
269
+ "name": "toolbar",
270
+ "type": "boolean",
271
+ "description": "",
272
+ "defaultValue": "true"
273
+ },
274
+ {
275
+ "name": "color",
276
+ "type": "ShipColor | null",
277
+ "description": "",
278
+ "defaultValue": "null"
279
+ },
280
+ {
281
+ "name": "variant",
282
+ "type": "'base' | 'type-b' | null",
283
+ "description": "",
284
+ "defaultValue": "'base'"
285
+ },
286
+ {
287
+ "name": "customCommands",
288
+ "type": "ShipEditorCommand[]",
289
+ "description": "",
290
+ "defaultValue": "[]"
291
+ },
292
+ {
293
+ "name": "slashCommands",
294
+ "type": "boolean | string[]",
295
+ "description": "",
296
+ "defaultValue": "true"
297
+ },
298
+ {
299
+ "name": "showFormats",
300
+ "type": "boolean",
301
+ "description": "",
302
+ "defaultValue": "true"
303
+ },
304
+ {
305
+ "name": "showBlocks",
306
+ "type": "boolean",
307
+ "description": "",
308
+ "defaultValue": "true"
309
+ },
310
+ {
311
+ "name": "showLists",
312
+ "type": "boolean",
313
+ "description": "",
314
+ "defaultValue": "true"
315
+ },
316
+ {
317
+ "name": "showAlignments",
318
+ "type": "boolean",
319
+ "description": "",
320
+ "defaultValue": "true"
321
+ },
322
+ {
323
+ "name": "showInsertions",
324
+ "type": "boolean",
325
+ "description": "",
326
+ "defaultValue": "true"
327
+ },
328
+ {
329
+ "name": "showHistory",
330
+ "type": "boolean",
331
+ "description": "",
332
+ "defaultValue": "true"
333
+ },
334
+ {
335
+ "name": "customUpload",
336
+ "type": "boolean",
337
+ "description": "",
338
+ "defaultValue": "false"
339
+ },
340
+ {
341
+ "name": "imageUploadEnabled",
342
+ "type": "boolean",
343
+ "description": "",
344
+ "defaultValue": "true"
345
+ },
346
+ {
347
+ "name": "value",
348
+ "type": "string | ShipEditorDocument | null",
349
+ "description": "",
350
+ "defaultValue": "''"
351
+ }
352
+ ],
353
+ "outputs": [
354
+ {
355
+ "name": "imageUpload",
356
+ "type": "File",
357
+ "description": ""
358
+ }
359
+ ],
360
+ "methods": [
361
+ {
362
+ "name": "execCommand",
363
+ "parameters": "commandId: string, showUI?: boolean, value?: string",
364
+ "returnType": "boolean;\n queryCommandState(commandId: string): boolean;\n queryCommandEnabled(commandId: string): boolean;\n}\n\n// Value Accessor Provider\nconst SHIP_EDITOR_VALUE_ACCESSOR: Provider =",
365
+ "description": ""
366
+ },
367
+ {
368
+ "name": "effect",
369
+ "parameters": "() => {\n this.showFormats();\n this.showBlocks();\n this.showLists();\n this.showAlignments();\n this.showInsertions();\n this.showHistory();\n this.toolbar();\n this.readonly();\n this.#initializeToolbarTabindexes();\n });\n }\n\n ngOnInit(",
370
+ "returnType": "any",
371
+ "description": ""
372
+ },
373
+ {
374
+ "name": "clearTimeout",
375
+ "parameters": "this.#typingTimeout);\n }\n\n if (saveImmediately",
376
+ "returnType": "any",
377
+ "description": ""
378
+ },
379
+ {
380
+ "name": "registerOnChange",
381
+ "parameters": "fn: (value: ShipEditorValue) => void",
382
+ "returnType": "void",
383
+ "description": ""
384
+ },
385
+ {
386
+ "name": "registerOnTouched",
387
+ "parameters": "fn: () => void",
388
+ "returnType": "void",
389
+ "description": ""
390
+ },
391
+ {
392
+ "name": "setDisabledState",
393
+ "parameters": "isDisabled: boolean",
394
+ "returnType": "void",
395
+ "description": ""
396
+ },
397
+ {
398
+ "name": "formatText",
399
+ "parameters": "command: string, value: string = ''",
400
+ "returnType": "any",
401
+ "description": ""
402
+ },
403
+ {
404
+ "name": "applyInlineStyle",
405
+ "parameters": "tag: string",
406
+ "returnType": "any",
407
+ "description": ""
408
+ },
409
+ {
410
+ "name": "while",
411
+ "parameters": "node",
412
+ "returnType": "any",
413
+ "description": ""
414
+ },
415
+ {
416
+ "name": "toggleLink",
417
+ "parameters": "url: string",
418
+ "returnType": "any",
419
+ "description": ""
420
+ },
421
+ {
422
+ "name": "setBlockType",
423
+ "parameters": "tag: string",
424
+ "returnType": "any",
425
+ "description": ""
426
+ },
427
+ {
428
+ "name": "toggleList",
429
+ "parameters": "listType: 'ul' | 'ol'",
430
+ "returnType": "any",
431
+ "description": ""
432
+ },
433
+ {
434
+ "name": "insertHorizontalRule",
435
+ "parameters": "",
436
+ "returnType": "any",
437
+ "description": ""
438
+ },
439
+ {
440
+ "name": "setAlign",
441
+ "parameters": "direction: 'left' | 'center' | 'right'",
442
+ "returnType": "any",
443
+ "description": ""
444
+ },
445
+ {
446
+ "name": "removeFormat",
447
+ "parameters": "",
448
+ "returnType": "any",
449
+ "description": ""
450
+ },
451
+ {
452
+ "name": "undo",
453
+ "parameters": "",
454
+ "returnType": "any",
455
+ "description": ""
456
+ },
457
+ {
458
+ "name": "openLinkModal",
459
+ "parameters": "",
460
+ "returnType": "any",
461
+ "description": ""
462
+ },
463
+ {
464
+ "name": "openImageModal",
465
+ "parameters": "",
466
+ "returnType": "any",
467
+ "description": ""
468
+ },
469
+ {
470
+ "name": "onComponentFocusIn",
471
+ "parameters": "event: FocusEvent",
472
+ "returnType": "any",
473
+ "description": ""
474
+ },
475
+ {
476
+ "name": "onComponentClick",
477
+ "parameters": "event: MouseEvent",
478
+ "returnType": "any",
479
+ "description": ""
480
+ },
481
+ {
482
+ "name": "onKeyDown",
483
+ "parameters": "event: KeyboardEvent",
484
+ "returnType": "any",
485
+ "description": ""
486
+ },
487
+ {
488
+ "name": "onKeyUp",
489
+ "parameters": "event: KeyboardEvent",
490
+ "returnType": "any",
491
+ "description": ""
492
+ },
493
+ {
494
+ "name": "executeCommand",
495
+ "parameters": "cmd: ShipEditorCommand",
496
+ "returnType": "any",
497
+ "description": ""
498
+ },
499
+ {
500
+ "name": "getBlockLabel",
501
+ "parameters": "",
502
+ "returnType": "string",
503
+ "description": ""
504
+ },
505
+ {
506
+ "name": "onComponentScroll",
507
+ "parameters": "event: Event",
508
+ "returnType": "any",
509
+ "description": ""
510
+ },
511
+ {
512
+ "name": "onWindowResize",
513
+ "parameters": "",
514
+ "returnType": "any",
515
+ "description": ""
516
+ },
517
+ {
518
+ "name": "updateImgToolbarPosition",
519
+ "parameters": "",
520
+ "returnType": "any",
521
+ "description": ""
522
+ },
523
+ {
524
+ "name": "onFileSelected",
525
+ "parameters": "event: Event",
526
+ "returnType": "any",
527
+ "description": ""
528
+ },
529
+ {
530
+ "name": "onDrop",
531
+ "parameters": "event: DragEvent",
532
+ "returnType": "any",
533
+ "description": ""
534
+ },
535
+ {
536
+ "name": "onPaste",
537
+ "parameters": "event: ClipboardEvent",
538
+ "returnType": "any",
539
+ "description": ""
540
+ },
541
+ {
542
+ "name": "insertImage",
543
+ "parameters": "url: string",
544
+ "returnType": "any",
545
+ "description": ""
546
+ },
547
+ {
548
+ "name": "getHTML",
549
+ "parameters": "",
550
+ "returnType": "string",
551
+ "description": ""
552
+ },
553
+ {
554
+ "name": "getMarkdown",
555
+ "parameters": "",
556
+ "returnType": "string",
557
+ "description": ""
558
+ },
559
+ {
560
+ "name": "getJSON",
561
+ "parameters": "",
562
+ "returnType": "ShipEditorDocument",
563
+ "description": ""
564
+ },
565
+ {
566
+ "name": "setHTML",
567
+ "parameters": "html: string",
568
+ "returnType": "any",
569
+ "description": ""
570
+ },
571
+ {
572
+ "name": "setMarkdown",
573
+ "parameters": "md: string",
574
+ "returnType": "any",
575
+ "description": ""
576
+ },
577
+ {
578
+ "name": "setJSON",
579
+ "parameters": "json: ShipEditorDocument",
580
+ "returnType": "any",
581
+ "description": ""
582
+ },
583
+ {
584
+ "name": "clear",
585
+ "parameters": "",
586
+ "returnType": "any",
587
+ "description": ""
588
+ },
589
+ {
590
+ "name": "traverse",
591
+ "parameters": "child);\n }\n\n return md;\n }\n\n // Markdown => HTML Converter\n #markdownToHTML(markdown: string",
592
+ "returnType": "string",
593
+ "description": ""
594
+ }
595
+ ],
596
+ "cssVariables": [
597
+ {
598
+ "name": "--editor-bg",
599
+ "defaultValue": "var(--base-1)"
600
+ },
601
+ {
602
+ "name": "--editor-code-bg",
603
+ "defaultValue": "var(--base-2)"
604
+ },
605
+ {
606
+ "name": "--editor-border-color",
607
+ "defaultValue": "var(--base-4)"
608
+ },
609
+ {
610
+ "name": "--editor-toolbar-bg",
611
+ "defaultValue": "rgb(from var(--base-1) r g b / 70%)"
612
+ },
613
+ {
614
+ "name": "--editor-toolbar-border",
615
+ "defaultValue": "var(--base-4)"
616
+ },
617
+ {
618
+ "name": "--editor-btn-hover",
619
+ "defaultValue": "var(--base-3)"
620
+ },
621
+ {
622
+ "name": "--editor-btn-active-bg",
623
+ "defaultValue": "var(--primary-3)"
624
+ },
625
+ {
626
+ "name": "--editor-btn-active-c",
627
+ "defaultValue": "var(--primary-11)"
628
+ },
629
+ {
630
+ "name": "--editor-footer-bg",
631
+ "defaultValue": "var(--base-2)"
632
+ },
633
+ {
634
+ "name": "--editor-p",
635
+ "defaultValue": "#{p2r(16)} #{p2r(20)}"
636
+ },
637
+ {
638
+ "name": "--editor-min-h",
639
+ "defaultValue": "#{p2r(220)}"
640
+ },
641
+ {
642
+ "name": "--editor-border-focus",
643
+ "defaultValue": "var(--primary-8)"
644
+ },
645
+ {
646
+ "name": "--editor-shadow-focus",
647
+ "defaultValue": "0 0 0 3px var(--primary-3)"
648
+ }
649
+ ],
650
+ "examples": [
651
+ {
652
+ "name": "sandbox-editor",
653
+ "html": "<div class=\"sandbox-container\">\n <div class=\"sandbox-sidebar\">\n <sh-card class=\"type-b control-card\">\n <h3>Options</h3>\n\n <div class=\"control-group\">\n <label class=\"control-label\">Storage Format</label>\n <sh-button-group size=\"small\" [value]=\"selectedFormat()\" (valueChange)=\"onFormatChange($event)\">\n <button value=\"html\">HTML</button>\n <button value=\"markdown\">Markdown</button>\n <button value=\"json\">JSON</button>\n </sh-button-group>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"readonly-toggle\" class=\"control-label\">Read-Only</label>\n <sh-toggle id=\"readonly-toggle\" [(checked)]=\"isReadonly\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"toolbar-toggle\" class=\"control-label\">Show Toolbar</label>\n <sh-toggle id=\"toolbar-toggle\" [(checked)]=\"showToolbar\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"image-upload-toggle\" class=\"control-label\">Image Upload</label>\n <sh-toggle id=\"image-upload-toggle\" [(checked)]=\"imageUploadEnabled\"></sh-toggle>\n </div>\n\n @if (showToolbar()) {\n <div class=\"control-group-section-title\">Toolbar Groups</div>\n\n <div class=\"control-group inline\">\n <label for=\"formats-toggle\" class=\"control-label\">Formats</label>\n <sh-toggle id=\"formats-toggle\" [(checked)]=\"showFormats\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"blocks-toggle\" class=\"control-label\">Blocks/Headings</label>\n <sh-toggle id=\"blocks-toggle\" [(checked)]=\"showBlocks\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"lists-toggle\" class=\"control-label\">Lists</label>\n <sh-toggle id=\"lists-toggle\" [(checked)]=\"showLists\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"align-toggle\" class=\"control-label\">Alignments</label>\n <sh-toggle id=\"align-toggle\" [(checked)]=\"showAlignments\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"insert-toggle\" class=\"control-label\">Insertions</label>\n <sh-toggle id=\"insert-toggle\" [(checked)]=\"showInsertions\"></sh-toggle>\n </div>\n\n <div class=\"control-group inline\">\n <label for=\"history-toggle\" class=\"control-label\">History/Code</label>\n <sh-toggle id=\"history-toggle\" [(checked)]=\"showHistory\"></sh-toggle>\n </div>\n }\n </sh-card>\n </div>\n\n <div class=\"sandbox-editor-wrapper\">\n <sh-editor\n [(value)]=\"editorValue\"\n [format]=\"selectedFormat()\"\n [readonly]=\"isReadonly()\"\n [toolbar]=\"showToolbar()\"\n [showFormats]=\"showFormats()\"\n [showBlocks]=\"showBlocks()\"\n [showLists]=\"showLists()\"\n [showAlignments]=\"showAlignments()\"\n [showInsertions]=\"showInsertions()\"\n [showHistory]=\"showHistory()\"\n [customCommands]=\"customEditorCommands()\"\n [imageUploadEnabled]=\"imageUploadEnabled()\"\n placeholder=\"Start writing your premium content here...\">\n <!-- Projected custom toolbar action -->\n <button type=\"button\" class=\"sh-editor-btn\" (click)=\"customAction()\" shTooltip=\"Projected Action (Star)\">\n <sh-icon>star</sh-icon>\n </button>\n </sh-editor>\n\n <sh-card class=\"type-b output-card\">\n <div class=\"output-header\">\n <h3>Database Output</h3>\n <button shButton variant=\"outlined\" size=\"small\" (click)=\"copyToClipboard()\">\n <sh-icon>copy</sh-icon>\n Copy\n </button>\n </div>\n <p class=\"output-desc\">This is exactly what would be saved in your database:</p>\n <pre class=\"serialized-preview\"><code>{{ serializedOutput() }}</code></pre>\n </sh-card>\n </div>\n</div>\n",
654
+ "ts": "import { ChangeDetectionStrategy, Component, computed, signal, viewChild } from '@angular/core';\nimport { ShipColor } from '@ship-ui/core';\nimport { ShipButton } from '@ship-ui/core/ship-button';\nimport { ShipButtonGroup } from '@ship-ui/core/ship-button-group';\nimport { ShipCard } from '@ship-ui/core/ship-card';\nimport { ShipEditor, ShipEditorCommand, ShipEditorValue } from '@ship-ui/core/ship-editor';\nimport { ShipIcon } from '@ship-ui/core/ship-icon';\nimport { ShipToggle } from '@ship-ui/core/ship-toggle';\n\n@Component({\n selector: 'app-sandbox-editor',\n standalone: true,\n imports: [ShipEditor, ShipButton, ShipCard, ShipToggle, ShipIcon, ShipButtonGroup],\n templateUrl: './sandbox-editor.html',\n styleUrl: './sandbox-editor.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SandboxEditor {\n // Current format selection\n selectedFormat = signal<'html' | 'markdown' | 'json'>('html');\n\n editor = viewChild(ShipEditor);\n\n // Editor states\n isReadonly = signal<boolean>(false);\n showToolbar = signal<boolean>(true);\n selectedColor = signal<ShipColor | null>(null);\n imageUploadEnabled = signal<boolean>(true);\n\n // Toolbar group configurations\n showFormats = signal<boolean>(true);\n showBlocks = signal<boolean>(true);\n showLists = signal<boolean>(true);\n showAlignments = signal<boolean>(true);\n showInsertions = signal<boolean>(true);\n showHistory = signal<boolean>(true);\n\n // Custom action for projected button\n customAction() {\n alert('Custom Projected Button Clicked!');\n }\n\n customEditorCommands = signal<ShipEditorCommand[]>([\n {\n id: 'highlight',\n label: 'Highlight Text',\n icon: 'star',\n description: 'Highlight selected text in yellow',\n action: (editor) => {\n editor.formatText('backColor', '#fff2b2');\n },\n },\n {\n id: 'info-callout',\n label: 'Info Callout',\n icon: 'terminal',\n description: 'Insert an info callout box',\n action: (editor) => {\n editor.formatText(\n 'insertHTML',\n '<div style=\"background-color: var(--primary-2); border-left: 4px solid var(--primary-9); padding: 12px 16px; margin: 12px 0; border-radius: 4px; color: var(--base-12);\"><strong>Info:</strong> Start typing callout contents here...</div>'\n );\n },\n },\n ]);\n\n // Pre-populated initial contents for each format\n initialHtml = `<h1>Ship WYSIWYG Editor</h1><p>Welcome! This is a <strong>config-driven</strong> rich-text editor designed to support flexible storage formats.</p><ul><li><strong>Two-way binding</strong> with <code>ControlValueAccessor</code></li><li>Instant conversion to <strong>HTML</strong>, <strong>Markdown</strong>, or <strong>JSON</strong></li><li>Sticky blur-toolbar, light/dark mode support, and word counting</li></ul><blockquote>\"A beautiful interface makes editing content a delight.\"</blockquote><hr><p>Try changing the storage format below to see the serialized output update in real time!</p>`;\n\n // Current value model\n editorValue = signal<ShipEditorValue>(this.initialHtml);\n\n // Stringified preview for database presentation\n serializedOutput = computed(() => {\n const val = this.editorValue();\n if (val === null || val === undefined) return '';\n if (typeof val === 'string') return val;\n return JSON.stringify(val, null, 2);\n });\n\n // Handle format swapping, converting the value appropriately\n onFormatChange(format: string | null) {\n if (format === 'html' || format === 'markdown' || format === 'json') {\n this.selectedFormat.set(format);\n }\n }\n\n // Copy serialized contents to clipboard\n copyToClipboard() {\n navigator.clipboard.writeText(this.serializedOutput());\n }\n}\n"
655
+ }
656
+ ]
657
+ },
234
658
  {
235
659
  "name": "ShipBlueprint",
236
660
  "selector": "sh-blueprint",
@@ -1532,8 +1956,20 @@
1532
1956
  },
1533
1957
  {
1534
1958
  "name": "cancelAnimationFrame",
1535
- "parameters": "this.#animationFrameRequest);\n }\n }\n}\n\n@Directive({\n selector: '[shSort]',\n standalone: true,\n host: {\n 'role': 'columnheader',\n '[class.sortable]': '!!shSort()',\n '[attr.tabindex]': 'shSort() ? \"0\" : null',\n '(click)': 'shSort() ? toggleSort(",
1536
- "returnType": "null',\n '(keydown.enter)': 'shSort() ? toggleSort() : null',\n '(keydown.space)': 'shSort() ? toggleSort($event) : null',\n '[attr.aria-sort]': 'shSort() ? ariaSort() : null',\n '[class.sort-asc]': 'sortAsc()',\n '[class.sort-desc]': 'sortDesc()',\n },\n})\nexport class ShipSort",
1959
+ "parameters": "this.#animationFrameRequest);\n }\n }\n}\n\n@Directive({\n selector: '[shSort]',\n standalone: true,\n host: {\n role: 'columnheader',\n '[class.sortable]': '!!shSort()',\n '[attr.tabindex]': 'shSort() ? \"0\" : null',\n '(click)': 'shSort() ? toggleSort(",
1960
+ "returnType": "null',\n '[attr.aria-sort]': 'shSort() ? ariaSort() : null',\n '[class.sort-asc]': 'sortAsc()',\n '[class.sort-desc]': 'sortDesc()',\n },\n})\nexport class ShipSort",
1961
+ "description": ""
1962
+ },
1963
+ {
1964
+ "name": "onKeyDown",
1965
+ "parameters": "event: KeyboardEvent",
1966
+ "returnType": "any",
1967
+ "description": ""
1968
+ },
1969
+ {
1970
+ "name": "onCleanup",
1971
+ "parameters": "() => observer.disconnect());\n }\n });\n\n bodyEffect = effect(() => {\n const body = this.tbody()?.nativeElement;\n const head = this.thead()?.nativeElement;\n\n if (!body || !head) return;\n\n const stickyHeaderHeight = this.stickyHeaderHeight();\n\n queueMicrotask(() => {\n const hasStickyRowHeaderStartElement = head.querySelectorAll('tr.sticky').length > 0;\n const stickyBodyRows = body.querySelectorAll('tr.sticky');\n const hasStickyRowStartElement = stickyBodyRows.length > 0;\n\n if (hasStickyRowStartElement && hasStickyRowHeaderStartElement",
1972
+ "returnType": "any",
1537
1973
  "description": ""
1538
1974
  },
1539
1975
  {
@@ -1565,6 +2001,12 @@
1565
2001
  "parameters": "row: any, col: ShipTableColumn",
1566
2002
  "returnType": "any",
1567
2003
  "description": ""
2004
+ },
2005
+ {
2006
+ "name": "formatDate",
2007
+ "parameters": "value: any",
2008
+ "returnType": "string",
2009
+ "description": ""
1568
2010
  }
1569
2011
  ],
1570
2012
  "cssVariables": [
@@ -2772,6 +3214,12 @@
2772
3214
  "parameters": "",
2773
3215
  "returnType": "any",
2774
3216
  "description": ""
3217
+ },
3218
+ {
3219
+ "name": "handleKeyDown",
3220
+ "parameters": "event: KeyboardEvent",
3221
+ "returnType": "any",
3222
+ "description": ""
2775
3223
  }
2776
3224
  ],
2777
3225
  "cssVariables": [],
@@ -4386,6 +4834,135 @@
4386
4834
  }
4387
4835
  ]
4388
4836
  },
4837
+ {
4838
+ "name": "ShipA11yKeybindingsDirective",
4839
+ "selector": "[shA11yKeybinding]",
4840
+ "path": "core/projects/ship-ui/ship-a11y-keybindings/ship-a11y-keybindings.directive.ts",
4841
+ "description": "### Overview & Accessibility\n\nThe A11y Keybindings engine provides a unified system for handling keyboard shortcuts. The\n`[shA11yKeybinding]` directive dynamically sets standard `aria-keyshortcuts` attributes\non host elements to announce active hotkeys to assistive technologies.\n\n### Provider Overrides\n\nDefault shortcuts registered by components can be overridden globally at the application level.\nProvide the `SHIP_A11Y_KEYBINDINGS_OVERRIDE` injection token with a mapping object:\n`&#123; provide: SHIP_A11Y_KEYBINDINGS_OVERRIDE, useValue: &#123; 'action.name': 'ctrl+s' &#125; &#125;`.\n\n### OS Adaptive Keys (Command vs Control)\n\nWe support the `ctrlOrCmd` modifier keyword in shortcuts. It maps dynamically at runtime:\nresolves to `Command (⌘)` on macOS and `Control (Ctrl)` on Windows/Linux. Display formats are also customized for each OS (e.g. `⌘⇧K` vs `Ctrl+Shift+K`).\n\n### Collisions & Input Handling\n\nWhen in `mode=\"global\"`, the directive automatically ignores keypresses that originate\nfrom input boxes, selects, textareas, or content-editables (unless modifier keys like Ctrl, Cmd, or Alt are pressed),\nensuring keyboard shortcuts do not interfere with typing.",
4842
+ "keywords": [
4843
+ "accessibility",
4844
+ "keybindings",
4845
+ "hotkeys",
4846
+ "shortcuts",
4847
+ "keyboard",
4848
+ "global",
4849
+ "local",
4850
+ "cmd",
4851
+ "ctrl",
4852
+ "modifiers",
4853
+ "focus"
4854
+ ],
4855
+ "inputs": [
4856
+ {
4857
+ "name": "mode",
4858
+ "type": "'global' | 'local'",
4859
+ "description": "The registered action name (e.g. 'table.next-page', 'dialog.close'). / shA11yKeybinding = input.required<string>(); /** Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown). Default is 'local'.",
4860
+ "defaultValue": "'local'"
4861
+ },
4862
+ {
4863
+ "name": "preventDefault",
4864
+ "type": "boolean",
4865
+ "description": "The registered action name (e.g. 'table.next-page', 'dialog.close'). / shA11yKeybinding = input.required<string>(); /** Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown). Default is 'local'. / mode = input<'global' | 'local'>('local'); /** Whether to prevent the default action when the keybinding matches.",
4866
+ "defaultValue": "true"
4867
+ },
4868
+ {
4869
+ "name": "stopPropagation",
4870
+ "type": "boolean",
4871
+ "description": "The registered action name (e.g. 'table.next-page', 'dialog.close'). / shA11yKeybinding = input.required<string>(); /** Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown). Default is 'local'. / mode = input<'global' | 'local'>('local'); /** Whether to prevent the default action when the keybinding matches. / preventDefault = input<boolean>(true); /** Whether to stop event propagation when the keybinding matches.",
4872
+ "defaultValue": "true"
4873
+ }
4874
+ ],
4875
+ "outputs": [
4876
+ {
4877
+ "name": "triggered",
4878
+ "type": "KeyboardEvent",
4879
+ "description": "The registered action name (e.g. 'table.next-page', 'dialog.close'). / shA11yKeybinding = input.required<string>(); /** Defines whether the keybinding listener is 'local' (host element keydown) or 'global' (window keydown). Default is 'local'. / mode = input<'global' | 'local'>('local'); /** Whether to prevent the default action when the keybinding matches. / preventDefault = input<boolean>(true); /** Whether to stop event propagation when the keybinding matches. / stopPropagation = input<boolean>(true); /** Event emitted when the keybinding is triggered."
4880
+ }
4881
+ ],
4882
+ "methods": [
4883
+ {
4884
+ "name": "effect",
4885
+ "parameters": "(onCleanup) => {\n if (this.mode() === 'global' && isPlatformBrowser(this.#platformId)",
4886
+ "returnType": "any",
4887
+ "description": ""
4888
+ },
4889
+ {
4890
+ "name": "onCleanup",
4891
+ "parameters": "() => {\n window.removeEventListener('keydown', listener);\n });\n }\n });\n }\n\n /**\n * Local keydown listener when mode is 'local'\n */\n @HostListener('keydown', ['$event'])\n onKeyDown(event: KeyboardEvent",
4892
+ "returnType": "void",
4893
+ "description": ""
4894
+ }
4895
+ ],
4896
+ "cssVariables": [],
4897
+ "examples": [
4898
+ {
4899
+ "name": "basic-keybindings",
4900
+ "html": "<div class=\"example-wrapper\">\n <div class=\"card-grid\">\n <div class=\"card\">\n <h3>Local Trigger</h3>\n <p class=\"description\">\n Only triggers when the button itself is focused.\n </p>\n <div class=\"shortcut-info\">\n Shortcut: <code class=\"kbd-badge\">{{ service.getDisplayShortcut('example.increment-local') }}</code>\n </div>\n <button \n shButton \n [shA11yKeybinding]=\"'example.increment-local'\" \n mode=\"local\" \n (triggered)=\"incrementLocal()\">\n Local Increment ({{ localCounter() }})\n </button>\n </div>\n\n <div class=\"card\">\n <h3>Global Trigger</h3>\n <p class=\"description\">\n Triggers from anywhere on the page (ignored when focused in inputs).\n </p>\n <div class=\"shortcut-info\">\n Shortcut: <code class=\"kbd-badge\">{{ service.getDisplayShortcut('example.increment-global') }}</code>\n </div>\n <button \n shButton \n variant=\"flat\"\n [shA11yKeybinding]=\"'example.increment-global'\" \n mode=\"global\" \n (triggered)=\"incrementGlobal()\">\n Global Increment ({{ globalCounter() }})\n </button>\n </div>\n </div>\n\n <div class=\"controls-panel\">\n <button shButton variant=\"outlined\" size=\"small\" (click)=\"changeShortcut()\">\n Swap Local Shortcut to: {{ service.getShortcut('example.increment-local') === 'enter' ? 'Space' : 'Enter' }}\n </button>\n </div>\n</div>\n",
4901
+ "ts": "import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { ShipA11yKeybindingsDirective, ShipA11yKeybindingsService } from '@ship-ui/core/ship-a11y-keybindings';\nimport { ShipButton } from '@ship-ui/core/ship-button';\n\n@Component({\n selector: 'app-basic-keybindings',\n imports: [ShipA11yKeybindingsDirective, ShipButton],\n templateUrl: './basic-keybindings.html',\n styleUrl: './basic-keybindings.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BasicKeybindingsComponent {\n readonly service = inject(ShipA11yKeybindingsService);\n\n localCounter = signal(0);\n globalCounter = signal(0);\n\n constructor() {\n // Register defaults for our example actions\n this.service.registerDefaults({\n 'example.increment-local': 'enter',\n 'example.increment-global': 'ctrlOrCmd+shift+s',\n });\n }\n\n incrementLocal() {\n this.localCounter.update((c) => c + 1);\n }\n\n incrementGlobal() {\n this.globalCounter.update((c) => c + 1);\n }\n\n changeShortcut() {\n // Override a keybinding at runtime\n const currentLocal = this.service.getShortcut('example.increment-local');\n const nextLocal = currentLocal === 'enter' ? 'space' : 'enter';\n\n this.service.registerOverrides({\n 'example.increment-local': nextLocal,\n });\n }\n}\n"
4902
+ }
4903
+ ]
4904
+ },
4905
+ {
4906
+ "name": "ShipA11yKeybindingsService",
4907
+ "selector": "ship-a11y-keybindings-service",
4908
+ "path": "core/projects/ship-ui/ship-a11y-keybindings/ship-a11y-keybindings.service.ts",
4909
+ "description": "### Overview & Accessibility\n\nThe A11y Keybindings engine provides a unified system for handling keyboard shortcuts. The\n`[shA11yKeybinding]` directive dynamically sets standard `aria-keyshortcuts` attributes\non host elements to announce active hotkeys to assistive technologies.\n\n### Provider Overrides\n\nDefault shortcuts registered by components can be overridden globally at the application level.\nProvide the `SHIP_A11Y_KEYBINDINGS_OVERRIDE` injection token with a mapping object:\n`&#123; provide: SHIP_A11Y_KEYBINDINGS_OVERRIDE, useValue: &#123; 'action.name': 'ctrl+s' &#125; &#125;`.\n\n### OS Adaptive Keys (Command vs Control)\n\nWe support the `ctrlOrCmd` modifier keyword in shortcuts. It maps dynamically at runtime:\nresolves to `Command (⌘)` on macOS and `Control (Ctrl)` on Windows/Linux. Display formats are also customized for each OS (e.g. `⌘⇧K` vs `Ctrl+Shift+K`).\n\n### Collisions & Input Handling\n\nWhen in `mode=\"global\"`, the directive automatically ignores keypresses that originate\nfrom input boxes, selects, textareas, or content-editables (unless modifier keys like Ctrl, Cmd, or Alt are pressed),\nensuring keyboard shortcuts do not interfere with typing.",
4910
+ "keywords": [
4911
+ "accessibility",
4912
+ "keybindings",
4913
+ "hotkeys",
4914
+ "shortcuts",
4915
+ "keyboard",
4916
+ "global",
4917
+ "local",
4918
+ "cmd",
4919
+ "ctrl",
4920
+ "modifiers",
4921
+ "focus"
4922
+ ],
4923
+ "inputs": [],
4924
+ "outputs": [],
4925
+ "methods": [
4926
+ {
4927
+ "name": "registerDefaults",
4928
+ "parameters": "defaults: Record<string, string>",
4929
+ "returnType": "void",
4930
+ "description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence."
4931
+ },
4932
+ {
4933
+ "name": "registerOverrides",
4934
+ "parameters": "overrides: Record<string, string>",
4935
+ "returnType": "void",
4936
+ "description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence. / registerDefaults(defaults: Record<string, string>): void { for (const [action, shortcut] of Object.entries(defaults)) { this.#defaults.set(action, shortcut); // If we don't have an override/binding for this action, set it as the active binding if (!this.#bindings.has(action)) { this.#bindings.set(action, shortcut); } } } /** Registers overrides for actions. Overwrites any existing or default bindings."
4937
+ },
4938
+ {
4939
+ "name": "getShortcut",
4940
+ "parameters": "action: string",
4941
+ "returnType": "string | undefined",
4942
+ "description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence. / registerDefaults(defaults: Record<string, string>): void { for (const [action, shortcut] of Object.entries(defaults)) { this.#defaults.set(action, shortcut); // If we don't have an override/binding for this action, set it as the active binding if (!this.#bindings.has(action)) { this.#bindings.set(action, shortcut); } } } /** Registers overrides for actions. Overwrites any existing or default bindings. / registerOverrides(overrides: Record<string, string>): void { for (const [action, shortcut] of Object.entries(overrides)) { this.#bindings.set(action, shortcut); } } /** Retrieves the active shortcut string for a registered action."
4943
+ },
4944
+ {
4945
+ "name": "getDefaultShortcut",
4946
+ "parameters": "action: string",
4947
+ "returnType": "string | undefined",
4948
+ "description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence. / registerDefaults(defaults: Record<string, string>): void { for (const [action, shortcut] of Object.entries(defaults)) { this.#defaults.set(action, shortcut); // If we don't have an override/binding for this action, set it as the active binding if (!this.#bindings.has(action)) { this.#bindings.set(action, shortcut); } } } /** Registers overrides for actions. Overwrites any existing or default bindings. / registerOverrides(overrides: Record<string, string>): void { for (const [action, shortcut] of Object.entries(overrides)) { this.#bindings.set(action, shortcut); } } /** Retrieves the active shortcut string for a registered action. / getShortcut(action: string): string | undefined { return this.#bindings.get(action); } /** Retrieves the default shortcut string for a registered action."
4949
+ },
4950
+ {
4951
+ "name": "getDisplayShortcut",
4952
+ "parameters": "action: string",
4953
+ "returnType": "string | undefined",
4954
+ "description": "Returns true if the platform is running on macOS. / get isMac(): boolean { if (!isPlatformBrowser(this.#platformId)) return false; return navigator.userAgent.toLowerCase().includes('mac'); } constructor() { this.registerDefaults(DEFAULT_KEYBINDINGS); if (this.#overrides) { this.registerOverrides(this.#overrides); } } /** Registers default shortcuts for actions. If an override already exists for a given action, the override takes precedence. / registerDefaults(defaults: Record<string, string>): void { for (const [action, shortcut] of Object.entries(defaults)) { this.#defaults.set(action, shortcut); // If we don't have an override/binding for this action, set it as the active binding if (!this.#bindings.has(action)) { this.#bindings.set(action, shortcut); } } } /** Registers overrides for actions. Overwrites any existing or default bindings. / registerOverrides(overrides: Record<string, string>): void { for (const [action, shortcut] of Object.entries(overrides)) { this.#bindings.set(action, shortcut); } } /** Retrieves the active shortcut string for a registered action. / getShortcut(action: string): string | undefined { return this.#bindings.get(action); } /** Retrieves the default shortcut string for a registered action. / getDefaultShortcut(action: string): string | undefined { return this.#defaults.get(action); } /** Gets a formatted, user-friendly shortcut string for display. E.g. 'ctrlOrCmd+Shift+KeyK' -> '⌘⇧K' on macOS, 'Ctrl+Shift+K' on Windows/Linux. Supports comma-separated multiple shortcuts (e.g. 'ArrowRight, d' -> 'ArrowRight, D')."
4955
+ }
4956
+ ],
4957
+ "cssVariables": [],
4958
+ "examples": [
4959
+ {
4960
+ "name": "basic-keybindings",
4961
+ "html": "<div class=\"example-wrapper\">\n <div class=\"card-grid\">\n <div class=\"card\">\n <h3>Local Trigger</h3>\n <p class=\"description\">\n Only triggers when the button itself is focused.\n </p>\n <div class=\"shortcut-info\">\n Shortcut: <code class=\"kbd-badge\">{{ service.getDisplayShortcut('example.increment-local') }}</code>\n </div>\n <button \n shButton \n [shA11yKeybinding]=\"'example.increment-local'\" \n mode=\"local\" \n (triggered)=\"incrementLocal()\">\n Local Increment ({{ localCounter() }})\n </button>\n </div>\n\n <div class=\"card\">\n <h3>Global Trigger</h3>\n <p class=\"description\">\n Triggers from anywhere on the page (ignored when focused in inputs).\n </p>\n <div class=\"shortcut-info\">\n Shortcut: <code class=\"kbd-badge\">{{ service.getDisplayShortcut('example.increment-global') }}</code>\n </div>\n <button \n shButton \n variant=\"flat\"\n [shA11yKeybinding]=\"'example.increment-global'\" \n mode=\"global\" \n (triggered)=\"incrementGlobal()\">\n Global Increment ({{ globalCounter() }})\n </button>\n </div>\n </div>\n\n <div class=\"controls-panel\">\n <button shButton variant=\"outlined\" size=\"small\" (click)=\"changeShortcut()\">\n Swap Local Shortcut to: {{ service.getShortcut('example.increment-local') === 'enter' ? 'Space' : 'Enter' }}\n </button>\n </div>\n</div>\n",
4962
+ "ts": "import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { ShipA11yKeybindingsDirective, ShipA11yKeybindingsService } from '@ship-ui/core/ship-a11y-keybindings';\nimport { ShipButton } from '@ship-ui/core/ship-button';\n\n@Component({\n selector: 'app-basic-keybindings',\n imports: [ShipA11yKeybindingsDirective, ShipButton],\n templateUrl: './basic-keybindings.html',\n styleUrl: './basic-keybindings.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BasicKeybindingsComponent {\n readonly service = inject(ShipA11yKeybindingsService);\n\n localCounter = signal(0);\n globalCounter = signal(0);\n\n constructor() {\n // Register defaults for our example actions\n this.service.registerDefaults({\n 'example.increment-local': 'enter',\n 'example.increment-global': 'ctrlOrCmd+shift+s',\n });\n }\n\n incrementLocal() {\n this.localCounter.update((c) => c + 1);\n }\n\n incrementGlobal() {\n this.globalCounter.update((c) => c + 1);\n }\n\n changeShortcut() {\n // Override a keybinding at runtime\n const currentLocal = this.service.getShortcut('example.increment-local');\n const nextLocal = currentLocal === 'enter' ? 'space' : 'enter';\n\n this.service.registerOverrides({\n 'example.increment-local': nextLocal,\n });\n }\n}\n"
4963
+ }
4964
+ ]
4965
+ },
4389
4966
  {
4390
4967
  "name": "ShipButton",
4391
4968
  "selector": "[shButton]",