@mohsinonxrm/pptb-commandbar-studio 0.5.2 → 0.6.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 (84) hide show
  1. package/CHANGELOG.md +363 -99
  2. package/LICENSE +661 -661
  3. package/README.md +343 -316
  4. package/SECURITY.md +62 -0
  5. package/dist/assets/BulkPublishModal-DqX2QmUD.js +29 -0
  6. package/dist/assets/CommandEditorPanel-DidGBtFC.js +1 -0
  7. package/dist/assets/CommandPalette-AuDfsF8l.js +1 -0
  8. package/dist/assets/ConflictDrawer-DxQ_jd-x.js +1 -0
  9. package/dist/assets/CrmParameterEditor-BdmP65mF.js +1 -0
  10. package/dist/assets/DiffViewer-BZuqvP4f.js +7 -0
  11. package/dist/assets/HideInsteadOfDeleteModal-BYHCTZrG.js +1 -0
  12. package/dist/assets/IconPickerModal-Bw0C7KwV.js +1 -0
  13. package/dist/assets/ImportXmlModal-B8NgiUDZ.js +1 -0
  14. package/dist/assets/LocalizationEditor-tmniX5PC.js +1 -0
  15. package/dist/assets/MonacoEditor-Bbg9u2FW.js +2 -0
  16. package/dist/assets/NewButtonModal-BVvlbCv4.js +1 -0
  17. package/dist/assets/NewGroupModal-CgQy-uo4.js +1 -0
  18. package/dist/assets/NewTabModal-B_2-kwAx.js +1 -0
  19. package/dist/assets/RuleEditorPanel-BkNwBgaP.js +1 -0
  20. package/dist/assets/ScalingEditorPanel-ezMkzvG1.js +1 -0
  21. package/dist/assets/TabDisplayRulesPanel-trzExQ1T.js +1 -0
  22. package/dist/assets/UrlComposerModal-BlVXHKRM.js +1 -0
  23. package/dist/assets/WebResourceBrowser-DaPOZQ5X.js +1 -0
  24. package/dist/assets/XmlDrawer-BI6Wy6kL.js +2 -0
  25. package/dist/assets/crmParameters-rgfZhhzN.js +1 -0
  26. package/dist/assets/cssMode-DdC7kBk-.js +1 -0
  27. package/dist/assets/{dnd-DHB99ZH7.js → dnd-fsSwRnQv.js} +2 -2
  28. package/dist/assets/{editor.worker-CdQrwHl8.js → editor.worker-1APKdqmi.js} +2 -2
  29. package/dist/assets/fluent-icons-DxKNRZ1A.js +1 -0
  30. package/dist/assets/fluent-ui-D7EtYwEg.js +43 -0
  31. package/dist/assets/{freemarker2-B8bME0pA.js → freemarker2-DDn-Wpd6.js} +1 -1
  32. package/dist/assets/{handlebars-zIyCxTW6.js → handlebars-Ck_SjSJx.js} +1 -1
  33. package/dist/assets/{html-B5hFqu3X.js → html-BaJUqhKx.js} +1 -1
  34. package/dist/assets/htmlMode-CnYhJfhG.js +1 -0
  35. package/dist/assets/index-Bu7BlRFk.js +139 -0
  36. package/dist/assets/{javascript-BQGsYMat.js → javascript-2UlV1Qbm.js} +1 -1
  37. package/dist/assets/{jsonMode-CR0b-9Un.js → jsonMode-BmSpB9yr.js} +1 -1
  38. package/dist/assets/{liquid-DLq8jbP8.js → liquid-CE2reFvH.js} +1 -1
  39. package/dist/assets/{lspLanguageFeatures-C3buAG32.js → lspLanguageFeatures-BvbzEddk.js} +2 -2
  40. package/dist/assets/{mdx-5ni22WXY.js → mdx-BvcZv3tC.js} +1 -1
  41. package/dist/assets/{monaco-BLZnQb2P.js → monaco-Bx8A1Vm1.js} +4 -4
  42. package/dist/assets/monaco-Dprb6f6S.css +1 -0
  43. package/dist/assets/{python-F20Pd69B.js → python-MTPSN2vk.js} +1 -1
  44. package/dist/assets/{razor-3xmsG6cc.js → razor-Ccq_CgR8.js} +1 -1
  45. package/dist/assets/ruleKinds-B9hACGaq.js +1 -0
  46. package/dist/assets/{server.browser-DfdiBs1P.js → server.browser-C3R9jebn.js} +1 -1
  47. package/dist/assets/{tsMode-Cyusk9p4.js → tsMode-2cv3qhqp.js} +1 -1
  48. package/dist/assets/{typescript-BXT-ECX3.js → typescript-BD6t7JEG.js} +1 -1
  49. package/dist/assets/webResourceService-CvyHBu7T.js +1 -0
  50. package/dist/assets/{xml-Cwzeco8o.js → xml-DJGPA7j9.js} +1 -1
  51. package/dist/assets/xmlValidator-D7JG-eNC.js +3 -0
  52. package/dist/assets/{yaml-BHUePD-R.js → yaml-Cpl0dvFM.js} +1 -1
  53. package/dist/icons/icon.svg +16 -0
  54. package/dist/index.html +24 -18
  55. package/index.html +18 -12
  56. package/package.json +18 -32
  57. package/dist/assets/BulkPublishModal-DlglMah4.js +0 -29
  58. package/dist/assets/CommandEditorPanel-4pYnil3i.js +0 -1
  59. package/dist/assets/CommandPalette-iYxzI2G_.js +0 -1
  60. package/dist/assets/ConflictDrawer-DBPRRkoa.js +0 -1
  61. package/dist/assets/DiffViewer-DFBJPZKA.js +0 -7
  62. package/dist/assets/IconPickerModal-Bg0Zc8UZ.js +0 -1
  63. package/dist/assets/ImportXmlModal-BccMoaDv.js +0 -3
  64. package/dist/assets/LocalizationEditor-DVZy2uLy.js +0 -1
  65. package/dist/assets/MonacoEditor-DFYisBG6.js +0 -2
  66. package/dist/assets/NewButtonModal-D5Hz1yx4.js +0 -1
  67. package/dist/assets/NewGroupModal-BgTVG-VJ.js +0 -1
  68. package/dist/assets/NewTabModal-7-ZPcPi-.js +0 -1
  69. package/dist/assets/RuleEditorPanel-BwQzOQBe.js +0 -1
  70. package/dist/assets/ScalingEditorPanel-B221zNLL.js +0 -1
  71. package/dist/assets/TabDisplayRulesPanel-drKzEO_E.js +0 -1
  72. package/dist/assets/UrlComposerModal-Bnifp4oB.js +0 -1
  73. package/dist/assets/WebResourceBrowser-qtcNTNge.js +0 -1
  74. package/dist/assets/XmlDrawer-CN941TbU.js +0 -2
  75. package/dist/assets/cssMode-DFSY9yCW.js +0 -1
  76. package/dist/assets/fluent-icons-DiKr_qRE.js +0 -1
  77. package/dist/assets/fluent-ui-kUA2U4Gr.js +0 -43
  78. package/dist/assets/htmlMode-BmPxEBms.js +0 -1
  79. package/dist/assets/index-DjHxPFpB.js +0 -6
  80. package/dist/assets/monaco-Dvl_Svmm.css +0 -1
  81. package/dist/assets/webResourceService-Ck07E3GN.js +0 -1
  82. package/dist/assets/xmlGenerator-DEaVrfZJ.js +0 -130
  83. package/dist/stats.html +0 -4949
  84. package/npm-shrinkwrap.json +0 -13253
package/README.md CHANGED
@@ -1,316 +1,343 @@
1
- # Command Bar Studio for Power Platform ToolBox
2
-
3
- A fully featured visual designer for Dataverse **classic ribbon / command bar
4
- customizations** — the spiritual successor to [Ribbon Workbench](https://www.develop1.net/public/rwb/ribbonworkbench.aspx)
5
- reimagined with React 18, Fluent UI v9, and live Dataverse integration via
6
- [Power Platform ToolBox](https://www.powerplatformtoolbox.com/).
7
-
8
- ![Version](https://img.shields.io/badge/version-0.5.0--beta-orange)
9
- ![React](https://img.shields.io/badge/React-18-61DAFB?logo=react)
10
- ![TypeScript](https://img.shields.io/badge/TypeScript-5.8-3178C6?logo=typescript)
11
- ![Fluent UI](https://img.shields.io/badge/Fluent%20UI-v9-0078D4?logo=microsoft)
12
- ![PPTB Types](https://img.shields.io/badge/%40pptb%2Ftypes-1.2.3-orange)
13
- ![License](https://img.shields.io/badge/license-AGPL--3.0--only-green)
14
- ![Tests](https://img.shields.io/badge/tests-148%20passing-brightgreen)
15
-
16
- > **Classic commands only.** CBS targets Dataverse `RibbonDiffXml` — the
17
- > classic commanding system. It does not modify Power Fx / Modern Commanding
18
- > defined in the Command Designer.
19
-
20
- ---
21
-
22
- ## ✨ Features
23
-
24
- ### 🎨 Visual Ribbon Canvas
25
- - **Classic ribbon view** — faithful tabbed ribbon with groups, large icon tiles, and @dnd-kit drag-and-drop reorder with a styled `DragOverlay` ghost (brand border, shadow, `grabbing` cursor) and animated 2 px drop-line indicators between button slots
26
- - **Modern command bar view** — flat command bar exactly as rendered in Unified Interface
27
- - **List view** — sortable data grid of all buttons with inline visibility toggle
28
- - All 4 ribbon locations: **Home Grid**, **Sub Grid**, **Form**, **Application**
29
- - Tab drag-to-reorder, inline rename (double-click), `+ Add tab`
30
- - Context menus on buttons and groups — hide/show, duplicate, move, rename, delete, override OOB
31
-
32
- ### 📋 Properties Pane
33
- - Identity: label, translations (multi-LCID), tooltip, icon picker, image16/32, kind, sequence, template alias
34
- - Command binding with action summary chips
35
- - Display rules + Enable rules pill lists with inline add/remove
36
- - Scaling summary with link to full editor
37
- - Advanced: form ribbon scope selector, `Mscrm.OpenRecordItem` override toggle, client-type filter shortcuts
38
- - Conflict banner — surfaces managed-solution provenance via solution-layer API
39
-
40
- ### ⚡ Command Editor
41
- - JavaScript function: library (with Browse… to Web Resource Browser), function name, positional CrmParameter editor
42
- - URL action: address, PassParams, WinMode, named CrmParameters with querystring key
43
- - **Power Fx expression** — inline Monaco editor (syntax-highlighted, word wrap) with variable hints (`ThisRecord`, `Selection`, `Self`) and async-rule note
44
- - Custom API / Web API action
45
- - Enable rules + Display rules binding
46
- - References tab showing exact buttons + tab/group breadcrumb
47
- - Versions tab from history
48
- - Security advisory: all command definitions are visible in browser source
49
-
50
- ### 📐 Rule Editor
51
- - All 30 RuleStep kinds from `RibbonTypes.xsd` — Enable rules and Display rules
52
- - Drag-to-reorder steps
53
- - OrRule with nested "Add nested step"
54
- - InvertResult + Default value toggles per step
55
- - Plain-English `summarizeRule()` output
56
- - CustomRule async note (Promise\<boolean\> on Unified Interface)
57
-
58
- ### 🔤 Localization Editor
59
- - Multi-LCID label / tooltip title / tooltip body per button
60
- - Locale chip row with active/inactive state
61
- - "Insert line break" (`&#x200b;&#x200b;`)
62
- - 20 built-in LCIDs; add any custom LCID by number
63
-
64
- ### 🖼️ Icon Picker
65
- - **Fluent icons** tab — 85 icon catalogue with category filter + regular/filled variant; auto-uploads selected icon as SVG web resource
66
- - **Web resources** tab — browseable list of SVG resources in the org with search
67
- - **Custom upload** tab — drag-and-drop SVG → create web resource
68
-
69
- ### 🗂️ Scaling Editor
70
- - MaxSize + Scale step sequence management
71
- - Document-order-based scaling behavior per MS docs (order of `<Scale>` elements, not Sequence values)
72
- - Validation: non-overlapping MaxSize/Scale ranges
73
-
74
- ### 🗃️ Tab Display Rules Editor
75
- - `EntityRule` (all 4 contexts) + `PageRule`
76
- - Info note: entity-scoped tabs can only use EntityRule; global tabs can use both
77
-
78
- ### 🌐 Web Resource Browser
79
- - Virtualized list with type filter, custom-only toggle, search
80
- - Upload new resource directly from the browser
81
- - Edit JS/CSS/XML files in Monaco
82
-
83
- ### 🔗 URL Action Composer
84
- - Base URL, PassParams (full 6-parameter table per MS docs), custom CrmParameters
85
- - Live URL preview; `&` → `&amp;` escaping handled automatically
86
- - WinMode: Navigate / Dialog / Popup
87
-
88
- ### 📊 XML Drawer + Diff Viewer
89
- - Per-location RibbonDiffXml view in Monaco (read-only + editable import path)
90
- - "Format XML" button
91
- - Monaco `DiffEditor` compare-to-baseline
92
- - CBS-specific completions: `$LocLabels:`, `$webresource:`, `Command=`, `ModernImage=`
93
-
94
- ### 🔄 Publish Pipeline
95
- - Correct delta-only `RibbonDiffXml` — only changed elements emitted
96
- - Solution ZIP packaging with `behavior=2` (shell-only) root component — does **not** pull in all entity subcomponents
97
- - 5-minute import polling with progress beats
98
- - Post-publish `ribbonmetadatasettoprocess` monitoring — surfaces compile errors that `publishCustomizations` silently swallows
99
- - `Mscrm.OpenRecordItem` override support — emitted as a system-command override when the user defines a JS action on it
100
-
101
- ### 🧩 Solution Conflict Drawer
102
- - Provenance detection via `msdyn_componentlayer` (falls back to namespace heuristic when unavailable)
103
- - Banner in properties pane for managed-solution buttons
104
- - Live solution-layer fetch in the drawer
105
-
106
- ### ⌨️ Keyboard Shortcuts + Command Palette
107
- - `Ctrl+K` — Command Palette (search buttons, commands, rules, entities, actions)
108
- - `Ctrl+Z/Y`, `Ctrl+Shift+Z` — Undo / Redo (80-entry stack)
109
- - `Ctrl+D` — Duplicate selected button
110
- - `Ctrl+S` — Open Bulk Publish
111
- - `Delete/Backspace` — Delete button (with OOB confirm)
112
- - `F2` — Inline rename tab / group
113
- - Arrow keys — navigate buttons on canvas
114
-
115
- ### 💾 Persistence
116
- - Auto-save ribbon state to `localStorage` keyed by org URL + entity
117
- - Restore-from-previous-session banner on startup
118
- - Pane widths, collapsed state, view mode all persisted
119
-
120
- ---
121
-
122
- ## 🖼️ Interface Overview
123
-
124
- ```
125
- ┌─────────────────────────────────────────────────────────────────┐
126
- │ TopBar: Solution picker │ Ctrl+K │ Undo/Redo │ Save │ Publish │
127
- │ SubHeader: Breadcrumb │ N changes since publish │ Preview … │
128
- ├──────────────────┬──────────────────────────────┬───────────────┤
129
- │ Left Pane │ Ribbon Canvas │ Properties │
130
- │ • Tables │ Location: Home/Sub/Form/App │ • Identity │
131
- │ (solution │ Tab strip (drag-reorder) │ • Command │
132
- │ scope only) │ View: Classic / Modern / List│ • Rules │
133
- │ • Solution │ Groups + Buttons (DnD) │ • Scaling │
134
- │ Elements │ │ • Advanced │
135
- │ – Buttons │ │ │
136
- │ – Commands │ │ │
137
- │ – Rules │ │ │
138
- │ – Templates │ │ │
139
- ├──────────────────┴──────────────────────────────┴───────────────┤
140
- │ Bottom Panel: Commands │ Display Rules │ Enable Rules │ History │
141
- │ │ Console (runtime + publish log) │
142
- └─────────────────────────────────────────────────────────────────┘
143
- ```
144
-
145
- ---
146
-
147
- ## 🚀 Getting Started
148
-
149
- Command Bar Studio runs **inside [Power Platform ToolBox](https://www.powerplatformtoolbox.com/)**. It requires the PPTB host to be available — authentication and Dataverse connectivity are provided by the host.
150
-
151
- 1. Install Power Platform ToolBox
152
- 2. Connect to a Dataverse environment
153
- 3. Open Command Bar Studio from the tool list
154
- 4. Pick an unmanaged solution from the TopBar
155
- 5. Select a table from the left pane
156
- 6. Edit ribbon buttons visually; click **Publish** when done
157
-
158
- ---
159
-
160
- ## 🛠️ Development
161
-
162
- ### Setup
163
-
164
- ```bash
165
- git clone https://github.com/mohsinonxrm/pptb-mxrm-commandbar-studio.git
166
- cd pptb-mxrm-commandbar-studio
167
- npm install
168
- ```
169
-
170
- ### Common scripts
171
-
172
- ```bash
173
- # Vite dev server (standalone — no PPTB host; ribbon APIs not available)
174
- npm run dev
175
-
176
- # TypeScript-only check (fast)
177
- npm run typecheck
178
-
179
- # Production build
180
- npm run build
181
-
182
- # Preview the production build locally
183
- npm run preview
184
-
185
- # Run tests
186
- npm test
187
-
188
- # Run tests with coverage
189
- npm run test:coverage
190
- ```
191
-
192
- ### Standalone vs PPTB-embedded
193
-
194
- CBS runs in two modes:
195
-
196
- - **Standalone** (`npm run dev`) — opens in a regular browser tab. Useful for UI
197
- iteration. All Dataverse calls fail because `window.dataverseAPI` is absent; the
198
- canvas and editors can still be exercised with the restore-from-localStorage path.
199
- - **Embedded in PPTB** — loads inside the PPTB iframe. The host injects
200
- `window.dataverseAPI` (typed) and `window.toolboxAPI`. All publish, load, and
201
- web-resource operations work against the active connection.
202
-
203
- ---
204
-
205
- ## 📦 Tech Stack
206
-
207
- | Technology | Version | Purpose |
208
- |---|---|---|
209
- | **React** | 18.3 | UI framework |
210
- | **TypeScript** | 5.8 | Strict mode, discriminated unions throughout |
211
- | **Vite** | 7 | Build tooling + HMR |
212
- | **Fluent UI v9** | 9.62 | Microsoft design system (Drawer, Dialog, DataGrid, Combobox…) |
213
- | **Monaco Editor** | 0.55 | XML / JS / diff editing across XmlDrawer, DiffViewer, Command Editor |
214
- | **@dnd-kit** | 6.3 | Drag-to-reorder for tabs, ribbon buttons (DragOverlay ghost + DroppableSlot indicators), and rule steps |
215
- | **@fluentui-contrib/react-data-grid-react-window** | 1.4 | Virtualized entity list + web resource browser |
216
- | **Zustand** | 5 | Global state (ribbons, selection, UI, session) |
217
- | **fflate** | 0.8 | ZIP compression for solution package + ribbon XML decompression |
218
- | **@pptb/types** | 1.2.3 | Power Platform ToolBox host API types (`window.dataverseAPI`, `window.toolboxAPI`) |
219
- | **Vitest** | 3 | Test runner — 148 tests across 8 suites |
220
-
221
- ---
222
-
223
- ## 📁 Project Layout
224
-
225
- ```
226
- src/
227
- components/
228
- shell/ — TopBar, SubHeader, Layout (resizable 4-pane)
229
- ribbon-canvas/ — RibbonCanvas, ClassicRibbon, ModernCommandBar, RibbonListView
230
- properties-pane/ — PropertiesPane + 6 section components
231
- bottom-panel/ — BottomPanel (Commands / Rules / History / Console tabs)
232
- panels/ — CommandEditor, RuleEditor, ScalingEditor, TabDisplayRules,
233
- ConflictDrawer, WebResourceBrowser, UrlComposer,
234
- DiffViewer, XmlDrawer
235
- modals/ — BulkPublish, IconPicker, LocalizationEditor, NewButton/Group/Tab,
236
- HideOobWarning, ImportXml, MoveToFlyout
237
- command-palette/ — CommandPalette (Ctrl+K)
238
- shared/ — MonacoEditor, FluentIcon, CrmParameterEditor,
239
- RuleStepEditor, RuleStepPicker, ResizableHandle
240
- services/
241
- xmlGenerator.ts — Delta-only RibbonDiffXml generator (all 30 rule kinds)
242
- xmlParser.ts — Merged ribbon XML parser (all 30 rule kinds)
243
- xmlValidator.ts — Pre-import validation with user-friendly messages
244
- dataverse/
245
- ribbonService.ts — RetrieveEntityRibbon / RetrieveApplicationRibbon
246
- publishService.ts — Solution ZIP packaging + import + ribbonmetadatasettoprocess poll
247
- ribbonProvenanceService.ts — msdyn_componentlayer solution-layer provenance
248
- conflictService.ts — fetchConflictLayersForElement
249
- webResourceService.ts — loadWebResources / loadWebResourceByName / loadWebResourceContent
250
- store/
251
- ribbonStore.ts — Ribbon state, 80-entry undo/redo, mutationsSincePublish
252
- selectionStore.ts — Selected location / tab / group / button
253
- uiStore.ts — Modal open states, pane widths, view mode (persisted)
254
- sessionStore.ts — Solutions, entities, connection, publisher prefix
255
- runtimeLogStore.ts — Console tab entries
256
- utils/
257
- ruleEngine.ts — summarizeRule() + evaluateRule() for all 30 kinds
258
- idGenerator.ts — Publisher-prefixed unique ribbon IDs
259
- webResourceBrowserCallback.ts — Module-level callback registry for WRB selection
260
- ribbonProvenance.ts — applyProvenanceToRibbons + heuristicOrigin
261
- monacoCompletions.ts — $LocLabels: / $webresource: / Command= completions
262
- types/
263
- ribbon.ts — Full domain model: RibbonButton, RuleStep (30 kinds), …
264
- __tests__/ — 148 Vitest tests
265
- __fixtures__/ — ribbon/account-full.xml, all-rule-kinds.xml, ribbondiff/…
266
- ```
267
-
268
- ---
269
-
270
- ## 🤝 Contributing
271
-
272
- Contributions are welcome! Please read the [Conventional Commits](https://www.conventionalcommits.org/) guide —
273
- all commits must follow the format enforced by `commitlint`.
274
-
275
- 1. Fork the repository
276
- 2. Create a feature branch: `git checkout -b feat/my-feature`
277
- 3. Commit: `git commit -m 'feat(canvas): add drag overlay'`
278
- 4. Push: `git push origin feat/my-feature`
279
- 5. Open a Pull Request → `dev`
280
-
281
- **Branch model:**
282
- - `dev` — working branch; all PRs target this
283
- - `main` — protected; only release-tagged merges
284
-
285
- ---
286
-
287
- ## 📋 Roadmap
288
-
289
- - [x] ~~ClassicRibbon drag migration to `@dnd-kit` with `DragOverlay` ghost~~ ✅ shipped in 0.5.0
290
- - [ ] `QueueUpdateRibbonClientMetadata` for targeted ribbon-only publish (vs. broad `publishCustomizations`)
291
- - [ ] Power Fx LSP / language server integration
292
- - [ ] Import error line highlighting in Monaco
293
- - [ ] Accessibility audit — verify full WCAG 2.1 AA compliance against spec §28
294
- - [ ] ClassicRibbon button drag: migrate from `useDraggable` to `useSortable` for live in-place reordering animation
295
-
296
- ---
297
-
298
- ## 📄 License
299
-
300
- Licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0-only)](LICENSE). See `LICENSE` for details.
301
-
302
- ---
303
-
304
- ## 🙏 Acknowledgments
305
-
306
- - **[Scott Durrow](https://www.develop1.net/)** — creator of [Ribbon Workbench](https://www.develop1.net/public/rwb/ribbonworkbench.aspx),
307
- the original community tool for Dataverse ribbon customization and the direct
308
- inspiration for Command Bar Studio. CBS exists to carry that work forward inside
309
- the Power Platform ToolBox ecosystem.
310
- - **[Power Platform ToolBox](https://github.com/PowerPlatformToolBox/desktop-app)** — host platform providing authenticated Dataverse access
311
- - **[Fluent UI](https://react.fluentui.dev/)** — Microsoft design system
312
- - **[Monaco Editor](https://microsoft.github.io/monaco-editor/)** — VS Code editor component
313
-
314
- ---
315
-
316
- Built with ❤️ for the Power Platform community
1
+ # Command Bar Studio for Power Platform ToolBox
2
+
3
+ A fully featured visual designer for Dataverse **classic ribbon / command bar
4
+ customizations** — the spiritual successor to [Ribbon Workbench](https://www.develop1.net/public/rwb/ribbonworkbench.aspx)
5
+ reimagined with React 18, Fluent UI v9, and live Dataverse integration via
6
+ [Power Platform ToolBox](https://www.powerplatformtoolbox.com/).
7
+
8
+ ![Version](https://raw.githubusercontent.com/mohsinonxrm/pptb-mxrm-commandbar-studio/refs/heads/main/assets/badges/version.svg)
9
+ ![Power Platform ToolBox 1.2.7 or later](https://raw.githubusercontent.com/mohsinonxrm/pptb-mxrm-commandbar-studio/refs/heads/main/assets/badges/pptb.svg)
10
+ ![@pptb/types 1.2.7](https://raw.githubusercontent.com/mohsinonxrm/pptb-mxrm-commandbar-studio/refs/heads/main/assets/badges/pptb-types.svg)
11
+ ![React 18](https://raw.githubusercontent.com/mohsinonxrm/pptb-mxrm-commandbar-studio/refs/heads/main/assets/badges/react.svg)
12
+ ![TypeScript 5.8](https://raw.githubusercontent.com/mohsinonxrm/pptb-mxrm-commandbar-studio/refs/heads/main/assets/badges/typescript.svg)
13
+ ![Fluent UI v9](https://raw.githubusercontent.com/mohsinonxrm/pptb-mxrm-commandbar-studio/refs/heads/main/assets/badges/fluent-ui.svg)
14
+ ![License AGPL-3.0-only](https://raw.githubusercontent.com/mohsinonxrm/pptb-mxrm-commandbar-studio/refs/heads/main/assets/badges/license.svg)
15
+
16
+ > **Classic commands only.** CBS targets Dataverse `RibbonDiffXml` — the
17
+ > classic commanding system. It does not modify Power Fx / Modern Commanding
18
+ > defined in the Command Designer.
19
+
20
+ ---
21
+
22
+ ## ✨ Features
23
+
24
+ ### 🎨 Visual Ribbon Canvas
25
+ - **Classic ribbon view** — faithful tabbed ribbon with groups, large icon tiles, and @dnd-kit drag-and-drop reorder with a styled `DragOverlay` ghost (brand border, shadow, `grabbing` cursor) and animated 2 px drop-line indicators between button slots
26
+ - **Modern command bar view** — flat command bar exactly as rendered in Unified Interface
27
+ - **List view** — sortable data grid of all buttons with inline visibility toggle
28
+ - All 4 ribbon locations: **Home Grid**, **Sub Grid**, **Form**, **Application**
29
+ - Tab drag-to-reorder, inline rename (double-click), `+ Add tab`
30
+ - Context menus on buttons and groups — hide/show, duplicate, move, rename, delete, override OOB
31
+
32
+ ### 📋 Properties Pane
33
+ - Identity: label, translations (multi-LCID), tooltip, icon picker, image16/32, kind, sequence, template alias
34
+ - Command binding with action summary chips
35
+ - Display rules + Enable rules pill lists with inline add/remove
36
+ - Scaling summary with link to full editor
37
+ - Advanced: form ribbon scope selector, `Mscrm.OpenRecordItem` override toggle, client-type filter shortcuts
38
+ - Conflict banner — surfaces managed-solution provenance via solution-layer API
39
+
40
+ ### ⚡ Command Editor
41
+ - JavaScript function: library (with Browse… to Web Resource Browser), function name, positional CrmParameter editor
42
+ - URL action: address, PassParams, WinMode, named CrmParameters with querystring key
43
+ - **Power Fx expression** — inline Monaco editor (syntax-highlighted, word wrap) with variable hints (`ThisRecord`, `Selection`, `Self`) and async-rule note
44
+ - Custom API / Web API action
45
+ - Enable rules + Display rules binding
46
+ - References tab showing exact buttons + tab/group breadcrumb
47
+ - Versions tab from history
48
+ - Security advisory: all command definitions are visible in browser source
49
+
50
+ ### 📐 Rule Editor
51
+ - All 30 RuleStep kinds from `RibbonTypes.xsd` — Enable rules and Display rules
52
+ - Drag-to-reorder steps
53
+ - OrRule with nested "Add nested step"
54
+ - InvertResult + Default value toggles per step
55
+ - Plain-English `summarizeRule()` output
56
+ - CustomRule async note (Promise\<boolean\> on Unified Interface)
57
+
58
+ ### 🔤 Localization Editor
59
+ - Multi-LCID label / tooltip title / tooltip body per button
60
+ - Locale chip row with active/inactive state
61
+ - "Insert line break" (`&#x200b;&#x200b;`)
62
+ - 20 built-in LCIDs; add any custom LCID by number
63
+
64
+ ### 🖼️ Icon Picker
65
+ - **Fluent icons** tab — 85 icon catalogue with category filter + regular/filled variant; auto-uploads selected icon as SVG web resource
66
+ - **Web resources** tab — browseable list of SVG resources in the org with search
67
+ - **Custom upload** tab — drag-and-drop SVG → create web resource
68
+
69
+ ### 🗂️ Scaling Editor
70
+ - MaxSize + Scale step sequence management
71
+ - Document-order-based scaling behavior per MS docs (order of `<Scale>` elements, not Sequence values)
72
+ - Validation: non-overlapping MaxSize/Scale ranges
73
+
74
+ ### 🗃️ Tab Display Rules Editor
75
+ - `EntityRule` (all 4 contexts) + `PageRule`
76
+ - Info note: entity-scoped tabs can only use EntityRule; global tabs can use both
77
+
78
+ ### 🌐 Web Resource Browser
79
+ - Virtualized list with type filter, custom-only toggle, search
80
+ - Upload new resource directly from the browser
81
+ - Edit JS/CSS/XML files in Monaco
82
+
83
+ ### 🔗 URL Action Composer
84
+ - Base URL, PassParams (full 6-parameter table per MS docs), custom CrmParameters
85
+ - Live URL preview; `&` → `&amp;` escaping handled automatically
86
+ - WinMode: Navigate / Dialog / Popup
87
+
88
+ ### 📊 XML Drawer + Diff Viewer
89
+ - Per-location RibbonDiffXml view in Monaco (read-only + editable import path)
90
+ - "Format XML" button
91
+ - Monaco `DiffEditor` compare-to-baseline
92
+ - CBS-specific completions: `$LocLabels:`, `$webresource:`, `Command=`, `ModernImage=`
93
+
94
+ ### 🔄 Publish Pipeline
95
+ - Read → merge → write: reads the table's existing unmanaged ribbon layer (CustomActions, commands, rules, labels) and merges your changes into it, so customizations made elsewhere (e.g. with Ribbon Workbench) are kept
96
+ - Pre-publish validation — blocking errors stop the import; warnings are logged
97
+ - Solution ZIP packaging with `behavior=2` (shell-only) root component — does **not** pull in all entity subcomponents
98
+ - Import → publish → re-import → publish, so Unified Interface picks up the change
99
+ - Waits and retries while the environment is busy with another import, publish or ribbon-metadata job
100
+ - Post-publish verification — reads the published layer back and checks every element this publish changed has the imported content
101
+ - `Mscrm.OpenRecordItem` override support — emitted as a system-command override when the user defines a JS action on it
102
+
103
+ ### 🧩 Solution Conflict Drawer
104
+ - Provenance detection via `msdyn_componentlayer` (falls back to namespace heuristic when unavailable)
105
+ - Banner in properties pane for managed-solution buttons
106
+ - Live solution-layer fetch in the drawer
107
+
108
+ ### ⌨️ Keyboard Shortcuts + Command Palette
109
+ - `Ctrl+K` — Command Palette (search buttons, commands, rules, entities, actions)
110
+ - `Ctrl+Z/Y`, `Ctrl+Shift+Z` — Undo / Redo (80-entry stack)
111
+ - `Ctrl+D` — Duplicate selected button
112
+ - `Ctrl+S` — Open Bulk Publish
113
+ - `Delete/Backspace` — Delete button (with OOB confirm)
114
+ - `F2` — Inline rename tab / group
115
+ - Arrow keys — navigate buttons on canvas
116
+
117
+ ### 💾 Persistence
118
+ - Unpublished changes are saved as a draft in `localStorage` after every change, per org,
119
+ solution and table: everything the publish needs (canvas, commands, rules, labels,
120
+ un-hides, customised / uncustomised commands). Only what differs from the loaded ribbon
121
+ is stored, compressed: a few KB per table
122
+ - When the table loads again, a dialog offers to **Restore** the draft on top of the ribbon
123
+ as loaded (one undo step) or **Discard** it. Drafts older than 30 days are discarded
124
+ - Switching table or solution with unpublished changes asks first: **Keep as draft**,
125
+ **Discard** or **Cancel**. When the browser storage is full, a banner says so and
126
+ **Keep as draft** is disabled, so the changes are never dropped silently
127
+ - Pane widths, collapsed state, view mode all persisted
128
+
129
+ ---
130
+
131
+ ## 🖼️ Interface Overview
132
+
133
+ ```
134
+ ┌─────────────────────────────────────────────────────────────────┐
135
+ │ TopBar: Solution picker │ Ctrl+K │ Undo/Redo │ Save │ Publish │
136
+ │ SubHeader: Breadcrumb │ N changes since publish │ Preview … │
137
+ ├──────────────────┬──────────────────────────────┬───────────────┤
138
+ │ Left Pane │ Ribbon Canvas │ Properties │
139
+ │ • Tables │ Location: Home/Sub/Form/App │ • Identity │
140
+ │ (solution │ Tab strip (drag-reorder) │ • Command │
141
+ │ scope only) │ View: Classic / Modern / List│ • Rules │
142
+ │ • Solution │ Groups + Buttons (DnD) │ • Scaling │
143
+ │ Elements │ │ • Advanced │
144
+ │ – Buttons │ │ │
145
+ │ – Commands │ │ │
146
+ │ – Rules │ │ │
147
+ │ – Templates │ │ │
148
+ ├──────────────────┴──────────────────────────────┴───────────────┤
149
+ │ Bottom Panel: Commands │ Display Rules │ Enable Rules │ History │
150
+ │ │ Console (runtime + publish log) │
151
+ └─────────────────────────────────────────────────────────────────┘
152
+ ```
153
+
154
+ ---
155
+
156
+ ## 🚀 Getting Started
157
+
158
+ Command Bar Studio runs **inside [Power Platform ToolBox](https://www.powerplatformtoolbox.com/)**. It requires the PPTB host to be available — authentication and Dataverse connectivity are provided by the host.
159
+
160
+ 1. Install Power Platform ToolBox **1.2.7 or later**
161
+ 2. Connect to a Dataverse environment
162
+ 3. Open Command Bar Studio from the tool list
163
+ 4. Pick an unmanaged solution from the TopBar
164
+ 5. Select a table from the left pane
165
+ 6. Edit ribbon buttons visually; click **Publish** when done
166
+
167
+ Icons you pick or upload, and scripts you save, are created as web resources and
168
+ added to the selected solution. A Fluent icon is uploaded as an SVG web resource named
169
+ `<prefix>_/icons/cbs/<icon>.svg` (`<icon>.filled.svg` for the filled variant).
170
+
171
+ If you turn on Dataverse impersonation for the tool in PPTB 1.2.7, web resources,
172
+ the solution import and the publish run as the impersonated user.
173
+
174
+ ---
175
+
176
+ ## 🛠️ Development
177
+
178
+ ### Setup
179
+
180
+ ```bash
181
+ git clone https://github.com/mohsinonxrm/pptb-mxrm-commandbar-studio.git
182
+ cd pptb-mxrm-commandbar-studio
183
+ npm install
184
+ ```
185
+
186
+ ### Common scripts
187
+
188
+ ```bash
189
+ # Vite dev server (standalone — no PPTB host; ribbon APIs not available)
190
+ npm run dev
191
+
192
+ # TypeScript-only check (fast)
193
+ npm run typecheck
194
+
195
+ # Production build
196
+ npm run build
197
+
198
+ # Preview the production build locally
199
+ npm run preview
200
+
201
+ # Run tests
202
+ npm test
203
+
204
+ # Lint (zero warnings allowed)
205
+ npm run lint
206
+
207
+ # Check / apply Prettier formatting
208
+ npm run format:check
209
+ npm run format
210
+
211
+ # Validate the PPTB tool manifest
212
+ npm run validate
213
+ ```
214
+
215
+ ### Standalone vs PPTB-embedded
216
+
217
+ CBS runs in two modes:
218
+
219
+ - **Standalone** (`npm run dev`) — opens in a regular browser tab. Useful for UI
220
+ iteration. All Dataverse calls fail because `window.dataverseAPI` is absent.
221
+ - **Dev harness** (`npm run dev:harness`) — the real UI in a browser tab against a
222
+ live Dataverse org, via a stubbed PPTB host and an Azure CLI–authenticated dev-server
223
+ proxy. See [CONTRIBUTING.md](CONTRIBUTING.md#running-against-a-live-org-dev-harness).
224
+ - **Embedded in PPTB** — loads inside the PPTB iframe. The host injects
225
+ `window.dataverseAPI` (typed) and `window.toolboxAPI`. All publish, load, and
226
+ web-resource operations work against the active connection.
227
+
228
+ ---
229
+
230
+ ## 📦 Tech Stack
231
+
232
+ | Technology | Version | Purpose |
233
+ |---|---|---|
234
+ | **React** | 18.3 | UI framework |
235
+ | **TypeScript** | 5.8 | Strict mode, discriminated unions throughout |
236
+ | **Vite** | 7 | Build tooling + HMR |
237
+ | **Fluent UI v9** | 9.62 | Microsoft design system (Drawer, Dialog, DataGrid, Combobox…) |
238
+ | **Monaco Editor** | 0.55 | XML / JS / diff editing across XmlDrawer, DiffViewer, Command Editor |
239
+ | **@dnd-kit** | 6.3 | Drag-to-reorder for tabs, ribbon buttons (DragOverlay ghost + DroppableSlot indicators), and rule steps |
240
+ | **@fluentui-contrib/react-data-grid-react-window** | 1.4 | Virtualized entity list + web resource browser |
241
+ | **Zustand** | 5 | Global state (ribbons, selection, UI, session) |
242
+ | **fflate** | 0.8 | ZIP compression for solution package + ribbon XML decompression |
243
+ | **@pptb/types** | 1.2.7 | Power Platform ToolBox host API types (`window.dataverseAPI`, `window.toolboxAPI`) |
244
+ | **Vitest** | 3 | Test runner — 148 tests across 8 suites |
245
+
246
+ ---
247
+
248
+ ## 📁 Project Layout
249
+
250
+ ```
251
+ src/
252
+ components/
253
+ shell/ — TopBar, SubHeader, Layout (resizable 4-pane)
254
+ ribbon-canvas/ — RibbonCanvas, ClassicRibbon, ModernCommandBar, RibbonListView
255
+ properties-pane/ — PropertiesPane + 6 section components
256
+ bottom-panel/ — BottomPanel (Commands / Rules / History / Console tabs)
257
+ panels/ — CommandEditor, RuleEditor, ScalingEditor, TabDisplayRules,
258
+ ConflictDrawer, WebResourceBrowser, UrlComposer,
259
+ DiffViewer, XmlDrawer
260
+ modals/ — BulkPublish, IconPicker, LocalizationEditor, NewButton/Group/Tab,
261
+ HideOobWarning, ImportXml, MoveToFlyout
262
+ command-palette/ — CommandPalette (Ctrl+K)
263
+ shared/ — MonacoEditor, FluentIcon, CrmParameterEditor,
264
+ RuleStepEditor, RuleStepPicker, ResizableHandle
265
+ services/
266
+ xmlGenerator.ts — RibbonDiffXml generator (all 30 rule kinds)
267
+ xmlParser.ts — Merged ribbon XML parser (all 30 rule kinds)
268
+ xmlValidator.ts — Pre-import validation with user-friendly messages
269
+ publishPlan.ts — What a publish changes per location (and what it can't)
270
+ ribbonDiffMerge.ts — Merges your changes into the existing unmanaged layer
271
+ dataverse/
272
+ ribbonService.ts — RetrieveEntityRibbon / RetrieveApplicationRibbon
273
+ publishService.ts — Layer merge, solution ZIP packaging, import, publish and verification
274
+ ribbonProvenanceService.ts — msdyn_componentlayer solution-layer provenance
275
+ conflictService.ts — fetchConflictLayersForElement
276
+ webResourceService.ts — loadWebResources / loadWebResourceByName / loadWebResourceContent
277
+ store/
278
+ ribbonStore.ts — Ribbon state, 80-entry undo/redo, mutationsSincePublish
279
+ selectionStore.ts — Selected location / tab / group / button
280
+ uiStore.ts — Modal open states, pane widths, view mode (persisted)
281
+ sessionStore.ts — Solutions, entities, connection, publisher prefix
282
+ runtimeLogStore.ts — Console tab entries
283
+ utils/
284
+ ruleEngine.ts — summarizeRule() + evaluateRule() for all 30 kinds
285
+ idGenerator.ts — Publisher-prefixed unique ribbon IDs
286
+ webResourceBrowserCallback.ts — Module-level callback registry for WRB selection
287
+ ribbonProvenance.ts — applyProvenanceToRibbons + heuristicOrigin
288
+ monacoCompletions.ts — $LocLabels: / $webresource: / Command= completions
289
+ types/
290
+ ribbon.ts — Full domain model: RibbonButton, RuleStep (30 kinds), …
291
+ __tests__/ — 148 Vitest tests
292
+ __fixtures__/ — ribbon/account-full.xml, all-rule-kinds.xml, ribbondiff/…
293
+ ```
294
+
295
+ ---
296
+
297
+ ## 🤝 Contributing
298
+
299
+ Contributions are welcome! Please use [Conventional Commits](https://www.conventionalcommits.org/)
300
+ style messages (`feat:`, `fix:`, `chore:` …).
301
+
302
+ 1. Fork the repository
303
+ 2. Create a feature branch: `git checkout -b feat/my-feature`
304
+ 3. Commit: `git commit -m 'feat(canvas): add drag overlay'`
305
+ 4. Push: `git push origin feat/my-feature`
306
+ 5. Open a Pull Request → `dev`
307
+
308
+ **Branch model:**
309
+ - `dev` — working branch; all PRs target this
310
+ - `main` — protected; only release-tagged merges
311
+
312
+ ---
313
+
314
+ ## 📋 Roadmap
315
+
316
+ - [x] ~~ClassicRibbon drag migration to `@dnd-kit` with `DragOverlay` ghost~~ ✅ shipped in 0.5.0
317
+ - [ ] `QueueUpdateRibbonClientMetadata` for targeted ribbon-only publish (vs. broad `publishCustomizations`)
318
+ - [ ] Power Fx LSP / language server integration
319
+ - [ ] Import error line highlighting in Monaco
320
+ - [ ] Accessibility audit — verify full WCAG 2.1 AA compliance against spec §28
321
+ - [ ] ClassicRibbon button drag: migrate from `useDraggable` to `useSortable` for live in-place reordering animation
322
+
323
+ ---
324
+
325
+ ## 📄 License
326
+
327
+ Licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0-only)](LICENSE). See `LICENSE` for details.
328
+
329
+ ---
330
+
331
+ ## 🙏 Acknowledgments
332
+
333
+ - **[Scott Durrow](https://www.develop1.net/)** — creator of [Ribbon Workbench](https://www.develop1.net/public/rwb/ribbonworkbench.aspx),
334
+ the original community tool for Dataverse ribbon customization and the direct
335
+ inspiration for Command Bar Studio. CBS exists to carry that work forward inside
336
+ the Power Platform ToolBox ecosystem.
337
+ - **[Power Platform ToolBox](https://github.com/PowerPlatformToolBox/desktop-app)** — host platform providing authenticated Dataverse access
338
+ - **[Fluent UI](https://react.fluentui.dev/)** — Microsoft design system
339
+ - **[Monaco Editor](https://microsoft.github.io/monaco-editor/)** — VS Code editor component
340
+
341
+ ---
342
+
343
+ Built with ❤️ for the Power Platform community