rook-cli 1.3.2 → 1.3.4

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 (89) hide show
  1. package/package.json +3 -2
  2. package/rook-framework/PRD-INSTALL-COMMAND.md +379 -0
  3. package/rook-framework/PRD.md +1214 -0
  4. package/rook-framework/README.md +143 -0
  5. package/rook-framework/assets/rk-accordion.js +99 -0
  6. package/rook-framework/assets/rk-alert-dialog.js +132 -0
  7. package/rook-framework/assets/rk-bottom-app-bar.js +88 -0
  8. package/rook-framework/assets/rk-carousel.js +145 -0
  9. package/rook-framework/assets/rk-collapsible.js +151 -0
  10. package/rook-framework/assets/rk-dialog.js +161 -0
  11. package/rook-framework/assets/rk-drawer.js +214 -0
  12. package/rook-framework/assets/rk-framework-core.css +2554 -0
  13. package/rook-framework/assets/rk-framework-tokens.css +101 -0
  14. package/rook-framework/assets/rk-modal.js +91 -0
  15. package/rook-framework/assets/rk-popover.js +264 -0
  16. package/rook-framework/assets/rk-progress.js +81 -0
  17. package/rook-framework/assets/rk-quantity.js +91 -0
  18. package/rook-framework/assets/rk-scroll-area.js +286 -0
  19. package/rook-framework/assets/rk-sheet.js +157 -0
  20. package/rook-framework/assets/rk-tabs.js +179 -0
  21. package/rook-framework/assets/rk-toggle.js +153 -0
  22. package/rook-framework/blocks/rk-accordion.liquid +97 -0
  23. package/rook-framework/blocks/rk-badge.liquid +103 -0
  24. package/rook-framework/blocks/rk-button.liquid +166 -0
  25. package/rook-framework/blocks/rk-divider.liquid +100 -0
  26. package/rook-framework/blocks/rk-form-field.liquid +120 -0
  27. package/rook-framework/blocks/rk-icon.liquid +134 -0
  28. package/rook-framework/blocks/rk-image.liquid +198 -0
  29. package/rook-framework/blocks/rk-installments.liquid +99 -0
  30. package/rook-framework/blocks/rk-pix-discount.liquid +99 -0
  31. package/rook-framework/blocks/rk-price.liquid +128 -0
  32. package/rook-framework/blocks/rk-quantity.liquid +108 -0
  33. package/rook-framework/blocks/rk-quick-add.liquid +137 -0
  34. package/rook-framework/blocks/rk-skeleton.liquid +104 -0
  35. package/rook-framework/blocks/rk-typography.liquid +183 -0
  36. package/rook-framework/config/rk-settings_schema.json +259 -0
  37. package/rook-framework/snippets/rk-accordion.liquid +31 -0
  38. package/rook-framework/snippets/rk-alert-dialog.liquid +83 -0
  39. package/rook-framework/snippets/rk-aspect-ratio.liquid +23 -0
  40. package/rook-framework/snippets/rk-badge.liquid +17 -0
  41. package/rook-framework/snippets/rk-bottom-app-bar.liquid +51 -0
  42. package/rook-framework/snippets/rk-button.liquid +49 -0
  43. package/rook-framework/snippets/rk-card.liquid +64 -0
  44. package/rook-framework/snippets/rk-carousel.liquid +74 -0
  45. package/rook-framework/snippets/rk-checkbox.liquid +34 -0
  46. package/rook-framework/snippets/rk-collapsible.liquid +52 -0
  47. package/rook-framework/snippets/rk-dialog.liquid +85 -0
  48. package/rook-framework/snippets/rk-divider.liquid +25 -0
  49. package/rook-framework/snippets/rk-drawer.liquid +81 -0
  50. package/rook-framework/snippets/rk-external-assets copy.liquid +33 -0
  51. package/rook-framework/snippets/rk-external-assets.liquid +68 -0
  52. package/rook-framework/snippets/rk-form-field.liquid +83 -0
  53. package/rook-framework/snippets/rk-gap-style.liquid +32 -0
  54. package/rook-framework/snippets/rk-icon.liquid +28 -0
  55. package/rook-framework/snippets/rk-image.liquid +60 -0
  56. package/rook-framework/snippets/rk-input.liquid +35 -0
  57. package/rook-framework/snippets/rk-installments.liquid +54 -0
  58. package/rook-framework/snippets/rk-item.liquid +69 -0
  59. package/rook-framework/snippets/rk-layout-style.liquid +37 -0
  60. package/rook-framework/snippets/rk-modal.liquid +31 -0
  61. package/rook-framework/snippets/rk-pix-discount.liquid +34 -0
  62. package/rook-framework/snippets/rk-popover.liquid +77 -0
  63. package/rook-framework/snippets/rk-price.liquid +48 -0
  64. package/rook-framework/snippets/rk-progress.liquid +38 -0
  65. package/rook-framework/snippets/rk-quantity.liquid +56 -0
  66. package/rook-framework/snippets/rk-quick-add.liquid +67 -0
  67. package/rook-framework/snippets/rk-scripts.liquid +17 -0
  68. package/rook-framework/snippets/rk-scroll-area.liquid +60 -0
  69. package/rook-framework/snippets/rk-sheet.liquid +86 -0
  70. package/rook-framework/snippets/rk-size-style.liquid +48 -0
  71. package/rook-framework/snippets/rk-skeleton.liquid +25 -0
  72. package/rook-framework/snippets/rk-spacing-padding.liquid +18 -0
  73. package/rook-framework/snippets/rk-spacing-style.liquid +54 -0
  74. package/rook-framework/snippets/rk-spinner.liquid +43 -0
  75. package/rook-framework/snippets/rk-swatch.liquid +33 -0
  76. package/rook-framework/snippets/rk-table.liquid +44 -0
  77. package/rook-framework/snippets/rk-tabs.liquid +52 -0
  78. package/rook-framework/snippets/rk-textarea.liquid +42 -0
  79. package/rook-framework/snippets/rk-toggle-group.liquid +27 -0
  80. package/rook-framework/snippets/rk-toggle.liquid +58 -0
  81. package/rook-framework/snippets/rk-typography.liquid +27 -0
  82. package/rook-framework/snippets/rk-variables.liquid +74 -0
  83. package/src/app.js +24 -0
  84. package/src/commands/InstallCommand.js +133 -0
  85. package/src/mcp/server.js +111 -1
  86. package/src/services/FrameworkInstaller.js +379 -0
  87. package/src/templates/block.liquid.txt +0 -15
  88. package/src/ui/PromptUI.js +15 -1
  89. package/src/utils/logger.js +1 -1
@@ -0,0 +1,151 @@
1
+ /* ==========================================================================
2
+ Rook UI Core — Collapsible Controller
3
+ Web Component: <rk-collapsible-element>
4
+ ========================================================================== */
5
+
6
+ if (!customElements.get('rk-collapsible-element')) {
7
+ class RkCollapsibleElement extends HTMLElement {
8
+ constructor() {
9
+ super();
10
+ this.trigger = null;
11
+ this.content = null;
12
+ this.isOpen = false;
13
+
14
+ this._onClick = this._onClick.bind(this);
15
+ this._handleExternalToggle = this._handleExternalToggle.bind(this);
16
+ }
17
+
18
+ connectedCallback() {
19
+ this.trigger = this.querySelector('.rk-collapsible__trigger');
20
+ this.content = this.querySelector('.rk-collapsible__content');
21
+
22
+ if (!this.content) return;
23
+
24
+ // State init
25
+ const defaultOpen = this.hasAttribute('data-open') && this.getAttribute('data-open') !== 'false';
26
+ this.isOpen = defaultOpen;
27
+
28
+ this._syncState(false); // sync without animation initially
29
+
30
+ // Event binding
31
+ if (this.trigger) {
32
+ this.trigger.addEventListener('click', this._onClick);
33
+ }
34
+
35
+ // Expose public API events listeners for external triggers
36
+ this.addEventListener('rk:collapsible:open', () => this.open());
37
+ this.addEventListener('rk:collapsible:close', () => this.close());
38
+ this.addEventListener('rk:collapsible:toggle', () => this.toggle());
39
+
40
+ const id = this.getAttribute('id');
41
+ if (id) {
42
+ document.addEventListener('click', this._handleExternalToggle);
43
+ }
44
+ }
45
+
46
+ disconnectedCallback() {
47
+ if (this.trigger) {
48
+ this.trigger.removeEventListener('click', this._onClick);
49
+ }
50
+ document.removeEventListener('click', this._handleExternalToggle);
51
+ }
52
+
53
+ /* ------------------------------------------------------------------ */
54
+ /* Public API */
55
+ /* ------------------------------------------------------------------ */
56
+
57
+ open() {
58
+ if (this.isOpen) return;
59
+ this.isOpen = true;
60
+ this._syncState(true);
61
+ }
62
+
63
+ close() {
64
+ if (!this.isOpen) return;
65
+ this.isOpen = false;
66
+ this._syncState(true);
67
+ }
68
+
69
+ toggle() {
70
+ if (this.isOpen) {
71
+ this.close();
72
+ } else {
73
+ this.open();
74
+ }
75
+ }
76
+
77
+ _onClick(e) {
78
+ e.preventDefault();
79
+ if (this.trigger && this.trigger.disabled) return;
80
+ this.toggle();
81
+ }
82
+
83
+ _handleExternalToggle(e) {
84
+ const externalBtn = e.target.closest(`[data-collapsible-toggle="${this.id}"]`);
85
+ if (externalBtn) {
86
+ e.preventDefault();
87
+ this.toggle();
88
+ }
89
+ }
90
+
91
+ /* ------------------------------------------------------------------ */
92
+ /* DOM Sync */
93
+ /* ------------------------------------------------------------------ */
94
+
95
+ _syncState(animate = true) {
96
+ // Manage classes for animation (CSS Grid 0fr -> 1fr approach)
97
+ if (this.isOpen) {
98
+ this.classList.add('rk-collapsible--open');
99
+ this.content.removeAttribute('hidden');
100
+
101
+ // Allow CSS transition to kick in if animating
102
+ requestAnimationFrame(() => {
103
+ this.content.classList.add('rk-collapsible__content--expanded');
104
+ });
105
+ } else {
106
+ this.classList.remove('rk-collapsible--open');
107
+ this.content.classList.remove('rk-collapsible__content--expanded');
108
+
109
+ // If not animating, hide immediately
110
+ if (!animate) {
111
+ this.content.setAttribute('hidden', 'true');
112
+ } else {
113
+ // Wait for CSS transition (typically 300ms) before hiding from screen readers
114
+ // We use an explicit listener or timeout. Timeout is safer if transition gets cancelled.
115
+ clearTimeout(this._hideTimeout);
116
+ this._hideTimeout = setTimeout(() => {
117
+ if (!this.isOpen) {
118
+ this.content.setAttribute('hidden', 'true');
119
+ }
120
+ }, 300); // Must match CSS duration
121
+ }
122
+ }
123
+
124
+ // A11y
125
+ if (this.trigger) {
126
+ this.trigger.setAttribute('aria-expanded', this.isOpen.toString());
127
+ }
128
+
129
+ // Focus management inside content
130
+ const focusables = this.content.querySelectorAll('a, button, input, select, textarea, [tabindex]');
131
+ focusables.forEach(el => {
132
+ if (this.isOpen) {
133
+ el.removeAttribute('tabindex');
134
+ } else {
135
+ // Force unfocusable
136
+ el.setAttribute('tabindex', '-1');
137
+ }
138
+ });
139
+
140
+ // Dispatch event
141
+ this.dispatchEvent(
142
+ new CustomEvent('rk:collapsible:change', {
143
+ bubbles: true,
144
+ detail: { isOpen: this.isOpen },
145
+ })
146
+ );
147
+ }
148
+ }
149
+
150
+ customElements.define('rk-collapsible-element', RkCollapsibleElement);
151
+ }
@@ -0,0 +1,161 @@
1
+ /* ==========================================================================
2
+ Rook UI Core — Dialog Controller
3
+ Web Component: <rk-dialog-element>
4
+ ========================================================================== */
5
+
6
+ if (!customElements.get('rk-dialog-element')) {
7
+ class RkDialogElement extends HTMLElement {
8
+ constructor() {
9
+ super();
10
+ this.dialog = null;
11
+ this.overlay = null;
12
+ this.content = null;
13
+ this.previousFocus = null;
14
+
15
+ this._onKeyDown = this._onKeyDown.bind(this);
16
+ }
17
+
18
+ connectedCallback() {
19
+ this.dialog = this.querySelector('.rk-dialog');
20
+ this.overlay = this.querySelector('.rk-dialog__overlay');
21
+ this.content = this.querySelector('.rk-dialog__content');
22
+
23
+ if (!this.dialog) return;
24
+
25
+ this._bindEvents();
26
+ this._setupOpenTriggers();
27
+ }
28
+
29
+ /* ------------------------------------------------------------------ */
30
+ /* Events */
31
+ /* ------------------------------------------------------------------ */
32
+
33
+ _bindEvents() {
34
+ // Close button
35
+ this.querySelectorAll('[data-action="close"]').forEach((el) => {
36
+ el.addEventListener('click', () => this.close());
37
+ });
38
+
39
+ // Overlay click (configurable)
40
+ if (this.overlay) {
41
+ const overlayClose = this.dataset.overlayClose !== 'false';
42
+ if (overlayClose) {
43
+ this.overlay.addEventListener('click', () => this.close());
44
+ }
45
+ }
46
+ }
47
+
48
+ _setupOpenTriggers() {
49
+ const dialogId = this.dataset.dialogId || this.dialog?.id;
50
+ if (!dialogId) return;
51
+
52
+ document.querySelectorAll(`[data-dialog-open="${dialogId}"]`).forEach((trigger) => {
53
+ trigger.addEventListener('click', (e) => {
54
+ e.preventDefault();
55
+ this.open();
56
+ });
57
+ });
58
+ }
59
+
60
+ /* ------------------------------------------------------------------ */
61
+ /* Public API */
62
+ /* ------------------------------------------------------------------ */
63
+
64
+ isOpen() {
65
+ return this.dialog?.classList.contains('rk-dialog--active');
66
+ }
67
+
68
+ open() {
69
+ if (!this.dialog || this.isOpen()) return;
70
+
71
+ this.previousFocus = document.activeElement;
72
+ this.dialog.classList.add('rk-dialog--active');
73
+ document.body.style.overflow = 'hidden';
74
+ document.addEventListener('keydown', this._onKeyDown);
75
+
76
+ // Focus first interactive element inside content
77
+ requestAnimationFrame(() => {
78
+ const firstFocusable = this.content?.querySelector(
79
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
80
+ );
81
+ if (firstFocusable) {
82
+ firstFocusable.focus();
83
+ }
84
+ });
85
+
86
+ this.dispatchEvent(
87
+ new CustomEvent('rk:dialog:open', {
88
+ bubbles: true,
89
+ detail: { id: this.dialog.id },
90
+ })
91
+ );
92
+ }
93
+
94
+ close() {
95
+ if (!this.dialog || !this.isOpen()) return;
96
+
97
+ this.dialog.classList.remove('rk-dialog--active');
98
+ document.body.style.overflow = '';
99
+ document.removeEventListener('keydown', this._onKeyDown);
100
+
101
+ if (this.previousFocus) {
102
+ this.previousFocus.focus();
103
+ this.previousFocus = null;
104
+ }
105
+
106
+ this.dispatchEvent(
107
+ new CustomEvent('rk:dialog:close', {
108
+ bubbles: true,
109
+ detail: { id: this.dialog.id },
110
+ })
111
+ );
112
+ }
113
+
114
+ /* ------------------------------------------------------------------ */
115
+ /* Keyboard handling (ESC + Tab trap) */
116
+ /* ------------------------------------------------------------------ */
117
+
118
+ _onKeyDown(e) {
119
+ if (!this.isOpen()) return;
120
+
121
+ // ESC to close
122
+ if (e.key === 'Escape') {
123
+ e.preventDefault();
124
+ this.close();
125
+ return;
126
+ }
127
+
128
+ // Tab trap
129
+ if (e.key === 'Tab') {
130
+ this._trapFocus(e);
131
+ }
132
+ }
133
+
134
+ _trapFocus(e) {
135
+ const focusable = this.content?.querySelectorAll(
136
+ 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
137
+ );
138
+
139
+ if (!focusable || focusable.length === 0) return;
140
+
141
+ const first = focusable[0];
142
+ const last = focusable[focusable.length - 1];
143
+
144
+ if (e.shiftKey) {
145
+ // Shift+Tab: if focus is on first, wrap to last
146
+ if (document.activeElement === first) {
147
+ e.preventDefault();
148
+ last.focus();
149
+ }
150
+ } else {
151
+ // Tab: if focus is on last, wrap to first
152
+ if (document.activeElement === last) {
153
+ e.preventDefault();
154
+ first.focus();
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ customElements.define('rk-dialog-element', RkDialogElement);
161
+ }
@@ -0,0 +1,214 @@
1
+ /* ==========================================================================
2
+ Rook UI Core — Drawer Controller
3
+ Web Component: <rk-drawer-element>
4
+ ========================================================================== */
5
+
6
+ if (!customElements.get('rk-drawer-element')) {
7
+ class RkDrawerElement extends HTMLElement {
8
+ constructor() {
9
+ super();
10
+ this.drawer = null;
11
+ this.overlay = null;
12
+ this.panel = null;
13
+ this.handle = null;
14
+ this.previousFocus = null;
15
+ this.focusableElements = [];
16
+
17
+ // Drag state
18
+ this._dragging = false;
19
+ this._startY = 0;
20
+ this._startX = 0;
21
+ this._currentTranslate = 0;
22
+ this._dismissThreshold = 0.3; // 30% of panel size to dismiss
23
+
24
+ this._onPointerMove = this._onPointerMove.bind(this);
25
+ this._onPointerUp = this._onPointerUp.bind(this);
26
+ }
27
+
28
+ connectedCallback() {
29
+ this.drawer = this.querySelector('.rk-drawer');
30
+ this.overlay = this.querySelector('.rk-drawer__overlay');
31
+ this.panel = this.querySelector('.rk-drawer__panel');
32
+ this.handle = this.querySelector('.rk-drawer__handle');
33
+
34
+ if (!this.drawer || !this.panel) return;
35
+
36
+ this._bindEvents();
37
+ this._setupOpenTriggers();
38
+ }
39
+
40
+ disconnectedCallback() {
41
+ document.removeEventListener('pointermove', this._onPointerMove);
42
+ document.removeEventListener('pointerup', this._onPointerUp);
43
+ }
44
+
45
+ /* ------------------------------------------------------------------ */
46
+ /* Events */
47
+ /* ------------------------------------------------------------------ */
48
+
49
+ _bindEvents() {
50
+ // Close via overlay click
51
+ if (this.overlay) {
52
+ this.overlay.addEventListener('click', () => this.close());
53
+ }
54
+
55
+ // Close buttons
56
+ this.querySelectorAll('[data-action="close"]').forEach((el) => {
57
+ el.addEventListener('click', () => this.close());
58
+ });
59
+
60
+ // ESC key
61
+ this._onKeyDown = (e) => {
62
+ if (e.key === 'Escape' && this.isOpen()) {
63
+ this.close();
64
+ }
65
+ };
66
+
67
+ // Drag-to-dismiss via handle or panel
68
+ if (this.handle) {
69
+ this.handle.addEventListener('pointerdown', (e) => this._onPointerDown(e));
70
+ }
71
+ }
72
+
73
+ _setupOpenTriggers() {
74
+ const drawerId = this.dataset.drawerId || this.drawer?.id;
75
+ if (!drawerId) return;
76
+
77
+ document.querySelectorAll(`[data-drawer-open="${drawerId}"]`).forEach((trigger) => {
78
+ trigger.addEventListener('click', (e) => {
79
+ e.preventDefault();
80
+ this.open();
81
+ });
82
+ });
83
+ }
84
+
85
+ /* ------------------------------------------------------------------ */
86
+ /* Public API */
87
+ /* ------------------------------------------------------------------ */
88
+
89
+ isOpen() {
90
+ return this.drawer?.classList.contains('rk-drawer--active');
91
+ }
92
+
93
+ open() {
94
+ if (!this.drawer || this.isOpen()) return;
95
+
96
+ this.previousFocus = document.activeElement;
97
+ this.drawer.classList.add('rk-drawer--active');
98
+ document.body.style.overflow = 'hidden';
99
+ document.addEventListener('keydown', this._onKeyDown);
100
+
101
+ // Reset any drag transform
102
+ this.panel.style.transform = '';
103
+ this.panel.style.transition = '';
104
+
105
+ // Trap focus
106
+ requestAnimationFrame(() => {
107
+ this.focusableElements = this.panel.querySelectorAll(
108
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
109
+ );
110
+ if (this.focusableElements.length > 0) {
111
+ this.focusableElements[0].focus();
112
+ }
113
+ });
114
+
115
+ this.dispatchEvent(
116
+ new CustomEvent('rk:drawer:open', { bubbles: true, detail: { id: this.drawer.id } })
117
+ );
118
+ }
119
+
120
+ close() {
121
+ if (!this.drawer || !this.isOpen()) return;
122
+
123
+ this.drawer.classList.remove('rk-drawer--active');
124
+ document.body.style.overflow = '';
125
+ document.removeEventListener('keydown', this._onKeyDown);
126
+
127
+ // Reset drag transform
128
+ this.panel.style.transform = '';
129
+ this.panel.style.transition = '';
130
+
131
+ if (this.previousFocus) {
132
+ this.previousFocus.focus();
133
+ this.previousFocus = null;
134
+ }
135
+
136
+ this.dispatchEvent(
137
+ new CustomEvent('rk:drawer:close', { bubbles: true, detail: { id: this.drawer.id } })
138
+ );
139
+ }
140
+
141
+ /* ------------------------------------------------------------------ */
142
+ /* Drag-to-dismiss */
143
+ /* ------------------------------------------------------------------ */
144
+
145
+ _getSide() {
146
+ return this.dataset.side || 'bottom';
147
+ }
148
+
149
+ _onPointerDown(e) {
150
+ if (!this.isOpen()) return;
151
+ e.preventDefault();
152
+
153
+ this._dragging = true;
154
+ this._startY = e.clientY;
155
+ this._startX = e.clientX;
156
+ this._currentTranslate = 0;
157
+ this.panel.style.transition = 'none';
158
+
159
+ document.addEventListener('pointermove', this._onPointerMove);
160
+ document.addEventListener('pointerup', this._onPointerUp);
161
+ }
162
+
163
+ _onPointerMove(e) {
164
+ if (!this._dragging) return;
165
+
166
+ const side = this._getSide();
167
+ let delta;
168
+
169
+ switch (side) {
170
+ case 'bottom':
171
+ delta = Math.max(0, e.clientY - this._startY);
172
+ this.panel.style.transform = `translateY(${delta}px)`;
173
+ break;
174
+ case 'top':
175
+ delta = Math.min(0, e.clientY - this._startY);
176
+ this.panel.style.transform = `translateY(${delta}px)`;
177
+ break;
178
+ case 'right':
179
+ delta = Math.max(0, e.clientX - this._startX);
180
+ this.panel.style.transform = `translateX(${delta}px)`;
181
+ break;
182
+ case 'left':
183
+ delta = Math.min(0, e.clientX - this._startX);
184
+ this.panel.style.transform = `translateX(${delta}px)`;
185
+ break;
186
+ }
187
+
188
+ this._currentTranslate = Math.abs(delta);
189
+ }
190
+
191
+ _onPointerUp() {
192
+ if (!this._dragging) return;
193
+ this._dragging = false;
194
+
195
+ document.removeEventListener('pointermove', this._onPointerMove);
196
+ document.removeEventListener('pointerup', this._onPointerUp);
197
+
198
+ const side = this._getSide();
199
+ const isVertical = side === 'top' || side === 'bottom';
200
+ const panelSize = isVertical ? this.panel.offsetHeight : this.panel.offsetWidth;
201
+
202
+ this.panel.style.transition = '';
203
+
204
+ if (this._currentTranslate > panelSize * this._dismissThreshold) {
205
+ this.close();
206
+ } else {
207
+ // Snap back
208
+ this.panel.style.transform = '';
209
+ }
210
+ }
211
+ }
212
+
213
+ customElements.define('rk-drawer-element', RkDrawerElement);
214
+ }