@mohsinonxrm/pptb-commandbar-studio 0.7.0-beta.3 → 0.8.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +141 -0
- package/README.md +13 -10
- package/dist/assets/BulkPublishModal-mJ41AWew.js +36 -0
- package/dist/assets/CommandEditorPanel-BiP1kPHY.js +1 -0
- package/dist/assets/CommandPalette-q51_0qRZ.js +1 -0
- package/dist/assets/{ConflictDrawer-DAQi31Y8.js → ConflictDrawer-CdWv_PbN.js} +1 -1
- package/dist/assets/{DiffViewer-DVIsuwXy.js → DiffViewer-BQP5f73U.js} +1 -1
- package/dist/assets/HideInsteadOfDeleteModal-C9r6ah5p.js +1 -0
- package/dist/assets/IconPickerModal-B81Tnf5i.js +1 -0
- package/dist/assets/{ImportXmlModal-2-0E4Ib_.js → ImportXmlModal-C0cMaS59.js} +1 -1
- package/dist/assets/LocalizationEditor-DniDtrZV.js +1 -0
- package/dist/assets/{MonacoEditor-DGzdcWNE.js → MonacoEditor-uzfuRCpJ.js} +1 -1
- package/dist/assets/{NewButtonModal-CKKbFpLd.js → NewButtonModal-BXpX1Ld5.js} +1 -1
- package/dist/assets/{NewGroupModal-BpXuxZKo.js → NewGroupModal-Bh7lXq52.js} +1 -1
- package/dist/assets/{NewTabModal-CoBSto1f.js → NewTabModal-Pntdk3in.js} +1 -1
- package/dist/assets/RuleEditorPanel-BUGKgc0N.js +1 -0
- package/dist/assets/{ScalingEditorPanel-L_j0ox4f.js → ScalingEditorPanel-p3-Yw3CO.js} +1 -1
- package/dist/assets/TabDisplayRulesPanel-CSNshwaw.js +1 -0
- package/dist/assets/{UrlComposerModal-BhcG5L1S.js → UrlComposerModal-WKSTwUcW.js} +1 -1
- package/dist/assets/WebResourceBrowser-BK-gVU2g.js +1 -0
- package/dist/assets/XmlDrawer-CQJElnD1.js +2 -0
- package/dist/assets/{dnd-BmqIZ1-i.js → dnd-Cq8OftAq.js} +1 -1
- package/dist/assets/fluent-icons-BqJj6rDi.js +1 -0
- package/dist/assets/{fluent-ui-Dde22_ZN.js → fluent-ui-QADO660t.js} +1 -1
- package/dist/assets/index-Di2_5czx.js +136 -0
- package/dist/assets/{useReplaceWebResourceConfirm-Mb36NTI7.js → useReplaceWebResourceConfirm-BUWBVNSC.js} +1 -1
- package/dist/assets/useStableKeys-p2OkzZWO.js +1 -0
- package/dist/assets/xmlValidator-BkeotXuB.js +3 -0
- package/dist/index.html +4 -4
- package/package.json +1 -1
- package/dist/assets/BulkPublishModal-B0Ecmiq1.js +0 -36
- package/dist/assets/CommandEditorPanel-FdBZ2zNj.js +0 -1
- package/dist/assets/CommandPalette-BL4yGqtx.js +0 -1
- package/dist/assets/CrmParameterEditor-CQD0gNNH.js +0 -1
- package/dist/assets/HideInsteadOfDeleteModal-BBfhx5dO.js +0 -1
- package/dist/assets/IconPickerModal-D97ew6l_.js +0 -1
- package/dist/assets/LocalizationEditor-7sYCHKMx.js +0 -1
- package/dist/assets/RuleEditorPanel-Dg2vnxrc.js +0 -1
- package/dist/assets/TabDisplayRulesPanel-W1SMJTlj.js +0 -1
- package/dist/assets/WebResourceBrowser-CQemLOXD.js +0 -1
- package/dist/assets/XmlDrawer-BOOdH0HN.js +0 -2
- package/dist/assets/fluent-icons-DUgjkNjy.js +0 -1
- package/dist/assets/index-DUEHURiA.js +0 -136
- package/dist/assets/xmlValidator-BB2o3QUU.js +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,147 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.8.0-beta.1] - 2026-10-25
|
|
13
|
+
|
|
14
|
+
The first beta of 0.8.0 (npm `beta`), on publish correctness and load performance:
|
|
15
|
+
- tables open faster;
|
|
16
|
+
- built-in buttons show captions and icons;
|
|
17
|
+
- the pre-publish check catches what the import would reject;
|
|
18
|
+
- saved web resources are published;
|
|
19
|
+
- tab reorders, duplicates and Kind changes publish as shown.
|
|
20
|
+
|
|
21
|
+
It includes everything in 0.7.0-beta.3. Requires Power Platform ToolBox 1.2.7 or later.
|
|
22
|
+
|
|
23
|
+
### ⚡ Performance
|
|
24
|
+
|
|
25
|
+
- **Tables open faster (#151, #4).**
|
|
26
|
+
- **Provenance:** working out which managed solution adds each control took one
|
|
27
|
+
`msdyn_componentlayers` query per component, one after another. That was 35–44 queries
|
|
28
|
+
(3.6–5.3 s) for account or contact, more on orgs with more apps, and the canvas waited for
|
|
29
|
+
it. Those queries also always came back empty. It's now one query of the managed ribbon
|
|
30
|
+
layers plus one for the solution names (~0.2 s), and managed buttons show their real
|
|
31
|
+
solution and publisher. That includes buttons in managed tabs, and application-ribbon
|
|
32
|
+
buttons on the table's own command bars (such as AI Builder's flyout).
|
|
33
|
+
- **Metadata:** opening a solution reads its tables' metadata 40 per request, instead of one
|
|
34
|
+
request per table (0.15 s instead of 0.8–1.7 s for 50–400 tables, and no throttling).
|
|
35
|
+
- **Application ribbon:** it's reused for 5 minutes when you switch tables in the same
|
|
36
|
+
environment. Retry or another environment reads it again. After a publish it's read on
|
|
37
|
+
every load for 5 minutes, because Dataverse can take that long to return the new version.
|
|
38
|
+
- **Progress:** the loading spinner names the table and the step it's on.
|
|
39
|
+
|
|
40
|
+
### ✨ Added
|
|
41
|
+
|
|
42
|
+
- **Built-in buttons show captions and icons (#6).** The canvas used to show keys such as
|
|
43
|
+
`Ribbon.HomepageGrid.MainTab.New` and a placeholder icon for most out-of-the-box buttons.
|
|
44
|
+
- **Captions:** the texts managed solutions define (for example "Focused view" or
|
|
45
|
+
"Learning Path") are read with each table, in the org's base language. The table
|
|
46
|
+
doesn't wait for them; the captions update when they arrive.
|
|
47
|
+
- **Platform keys:** Dataverse resolves these itself, and CBS can't read them yet (see
|
|
48
|
+
below). They show their last meaningful segment instead: `…MainTab.New` → "New",
|
|
49
|
+
`…View.TabName` → "View".
|
|
50
|
+
- **Icons:** about 80 out-of-the-box `ModernImage` names (New, Export to Excel, Flows,
|
|
51
|
+
Email a Link, Assign, …) get Fluent icons. A button whose icon is a `$webresource:`
|
|
52
|
+
image shows that image.
|
|
53
|
+
- **Only references:** only a label that refers to a text CBS can't read gets a caption.
|
|
54
|
+
Literal labels, and controls without one, are shown as they are.
|
|
55
|
+
- **Display only:** what publishes is the label and icon as loaded.
|
|
56
|
+
|
|
57
|
+
The real texts of platform keys only come from `GetClientMetadata`, a 162 MB response,
|
|
58
|
+
which is more than Power Platform ToolBox passes to a tool (128 MB).
|
|
59
|
+
|
|
60
|
+
### 🔧 Fixed
|
|
61
|
+
|
|
62
|
+
- **Reordering tabs publishes (#163).** Dragging a tab changed only the canvas, so the
|
|
63
|
+
publish had nothing to publish and the order reverted. A new tab, or one your
|
|
64
|
+
customizations add, now gets a Sequence between its neighbours, and that Sequence is
|
|
65
|
+
published. Reordering another solution's tabs is reported, since only their Sequence
|
|
66
|
+
orders them. When there's no free Sequence between two tabs, the tab is shown where it
|
|
67
|
+
will publish.
|
|
68
|
+
- **Duplicates are placed where they're shown (#164).**
|
|
69
|
+
- **Placement:** a copied button, menu item or group gets a Sequence between its
|
|
70
|
+
original and the next item (it used to get the original's + 5, which could collide
|
|
71
|
+
or pass the neighbour). When built-in neighbours leave no room, the copy is shown
|
|
72
|
+
where it will publish.
|
|
73
|
+
- **Ids:** a copy of another solution's element starts with your publisher prefix.
|
|
74
|
+
- **Solution Elements:** a button dropped from there goes after the group's last button.
|
|
75
|
+
- **Pre-publish check:** it warns when sibling customizations share a Sequence.
|
|
76
|
+
- **A dynamic flyout's PopulateQueryCommand is checked (#133).** A command that's misspelled
|
|
77
|
+
or defined nowhere, which left the drop-down empty, is reported. A copied flyout
|
|
78
|
+
carries its populate command.
|
|
79
|
+
- **Smaller fixes:**
|
|
80
|
+
- A ribbon package without `RibbonXml.xml` fails with a clear error instead of loading
|
|
81
|
+
an empty ribbon (#168).
|
|
82
|
+
- An imported group's `Command` (and its definition) and a Scale step's `PopupSize` are
|
|
83
|
+
no longer dropped (#169).
|
|
84
|
+
- Starting a Download in the XML drawer clears an earlier Copy message (#150).
|
|
85
|
+
- **Saved web resources are published (#156).** Saving a command's script in the command
|
|
86
|
+
editor, or replacing a file through **Upload**, updated the web resource but didn't publish
|
|
87
|
+
it, so users kept the old script or icon until someone ran Publish All. They're published
|
|
88
|
+
now. If the publish fails, the save is kept and CBS says so.
|
|
89
|
+
- **Translations and Properties agree on the base language (#157).** The Translations
|
|
90
|
+
dialog's base-language row edited a copy that the publish then overwrote with the
|
|
91
|
+
Properties text. It now edits the button's own text; other languages are unchanged.
|
|
92
|
+
- **Web resources are always referenced with `$webresource:` (#158).** That's the only form
|
|
93
|
+
that makes the solution depend on the web resource.
|
|
94
|
+
- A custom rule's library and a typed image path get the directive.
|
|
95
|
+
- A typed `/WebResources/…` image path becomes `$webresource:…`.
|
|
96
|
+
- A loaded `/WebResources/…` library is kept as is, and the pre-publish check warns about
|
|
97
|
+
it.
|
|
98
|
+
- Platform (`/_imgs/…`) and relative paths, and URLs, are kept as they are.
|
|
99
|
+
- **A typed `$LocLabels:` reference is kept as is (#159).** A label that CBS doesn't have was
|
|
100
|
+
published as a placeholder whose text was its own id. It's now written as a reference,
|
|
101
|
+
like another solution's label, and the pre-publish check warns when nothing defines it.
|
|
102
|
+
The XML import's message about it no longer says the publish would fail.
|
|
103
|
+
- **Duplicating a built-in group keeps its title.** A group titled by a platform text
|
|
104
|
+
(`$Resources:`) or another solution's label was copied with the key itself as its title
|
|
105
|
+
("Ribbon.HomepageGrid.MainTab.Management (copy)"), and that text was published. Like a
|
|
106
|
+
copied button, the copy now keeps the title as it is, so it shows the same text as the
|
|
107
|
+
original. Other groups' copies are still titled "… (copy)". To retitle such a copy, rename
|
|
108
|
+
it before you publish: once published, only titles of your own can be renamed. The
|
|
109
|
+
Console says so when you duplicate.
|
|
110
|
+
- **The pre-publish check catches what the import would reject (#153).** These used to fail
|
|
111
|
+
only at import, after CBS had started writing. Now they're found first, and nothing is
|
|
112
|
+
written:
|
|
113
|
+
- a rule with no steps;
|
|
114
|
+
- an OrRule with one alternative;
|
|
115
|
+
- a JavaScript action or custom rule without a valid function name or library;
|
|
116
|
+
- a URL action without an address or with an unnamed parameter;
|
|
117
|
+
- a rule step with a required value left empty;
|
|
118
|
+
- a number that isn't one.
|
|
119
|
+
|
|
120
|
+
The same problems in customizations that are already published are warnings. A cleared
|
|
121
|
+
number parameter now keeps its last value instead of being written as `NaN`.
|
|
122
|
+
- **Changing a control's Kind writes valid XML (#154).**
|
|
123
|
+
- A control keeps only the attributes its new element allows. For example, a flyout
|
|
124
|
+
turned into a button no longer carries `PopulateDynamically`.
|
|
125
|
+
- A menu item can be a button or a flyout, which is all a menu can hold.
|
|
126
|
+
- A loaded control's Kind is locked: changing it would replace the control another
|
|
127
|
+
solution or an earlier publish defined. Add a new control instead.
|
|
128
|
+
- **A tab has one display rule (#155).** Adding a second rule replaced the first, so a new
|
|
129
|
+
tab could disappear from where it was meant to show.
|
|
130
|
+
- **Tab rules** now edits a single rule, and the tab shows where any of its steps
|
|
131
|
+
matches.
|
|
132
|
+
- **Applies to** and **Context** can be **Any**.
|
|
133
|
+
- A table's tab picks that table or any, instead of a free-text name.
|
|
134
|
+
- A tab your customizations don't add is read-only.
|
|
135
|
+
- A new tab with no steps says it gets the default rule when you publish.
|
|
136
|
+
- **Publishing all customizations needs its own confirmation (#109).** **Also publish all
|
|
137
|
+
customizations** publishes every unpublished customization in the environment, including
|
|
138
|
+
other people's work in progress. It now shows a warning naming the environment, and
|
|
139
|
+
**Publish** waits for you to confirm it. The **Publish comment**, which was collected and
|
|
140
|
+
then dropped, is written to the Console log with the publish.
|
|
141
|
+
- **The top bar has no controls that do nothing (#22).**
|
|
142
|
+
- The **State simulator**, **Save** and **Settings** controls are removed. Changes are
|
|
143
|
+
already kept as a draft for each table automatically, and Ctrl+S opens Publish.
|
|
144
|
+
- **Help** now shows the shortcuts and copies the documentation link.
|
|
145
|
+
- **Narrow panes (#22).**
|
|
146
|
+
- When the window is narrow, the Tables and Properties panes give way so the canvas
|
|
147
|
+
keeps at least 40% of the width, as far as their minimum widths allow. In an 800 px pane it used to show one group at a
|
|
148
|
+
time.
|
|
149
|
+
- The top bar's solution name and search box shorten instead of overlapping.
|
|
150
|
+
- **Command editor (#22):** removing an action no longer hands its card's state to the next
|
|
151
|
+
one.
|
|
152
|
+
|
|
12
153
|
## [0.7.0-beta.3] - 2026-10-24
|
|
13
154
|
|
|
14
155
|
The third beta of 0.7.0 (npm `beta`): one fix. The **Show on Unified Interface** filter added a
|
package/README.md
CHANGED
|
@@ -26,13 +26,14 @@ reimagined with React 18, Fluent UI v9, and live Dataverse integration via
|
|
|
26
26
|
- **Modern command bar view** — flat command bar exactly as rendered in Unified Interface
|
|
27
27
|
- **List view** — sortable data grid of all buttons with inline visibility toggle
|
|
28
28
|
- All 4 ribbon locations: **Home Grid**, **Sub Grid**, **Form**, **Application**
|
|
29
|
+
- Built-in buttons show their captions (managed solutions' label texts; a platform key shows its last segment, e.g. "New") and icons (out-of-the-box `ModernImage` names, `$webresource:` images)
|
|
29
30
|
- Tab drag-to-reorder, inline rename (double-click), `+ Add tab`
|
|
30
31
|
- Context menus on buttons and groups — hide/show, duplicate, move, rename, delete, override OOB
|
|
31
|
-
- A copy (duplicate, customise button) keeps the labels another solution defines, so it shows the same text; edit its text to give it labels of its own
|
|
32
|
+
- A copy (duplicate, customise button) keeps the labels another solution defines, so it shows the same text; edit its text to give it labels of its own. A duplicated group with a platform or another solution's title keeps it: rename the copy before publishing if it should read differently
|
|
32
33
|
- If your unmanaged ribbon customizations can't be read when a table loads, a warning says so (labels may show as ids, ownership is unknown), with **Retry**
|
|
33
34
|
|
|
34
35
|
### 📋 Properties Pane
|
|
35
|
-
- Identity: label, translations (multi-LCID), tooltip, icon picker, image16/32, kind, sequence, template alias
|
|
36
|
+
- Identity: label, translations (multi-LCID), tooltip, icon picker, image16/32, kind (for new controls; a menu item is a button or a flyout), sequence, template alias
|
|
36
37
|
- Translations edit the labels a button publishes to; another solution's label, shared with every control that uses it, is read-only there
|
|
37
38
|
- Command binding with action summary chips
|
|
38
39
|
- Display rules + Enable rules pill lists with inline add/remove
|
|
@@ -75,8 +76,10 @@ reimagined with React 18, Fluent UI v9, and live Dataverse integration via
|
|
|
75
76
|
- Validation: non-overlapping MaxSize/Scale ranges
|
|
76
77
|
|
|
77
78
|
### 🗃️ Tab Display Rules Editor
|
|
78
|
-
-
|
|
79
|
+
- One display rule per tab; the tab shows where any of its steps matches
|
|
80
|
+
- `EntityRule` (all 4 contexts, or any) + `PageRule`
|
|
79
81
|
- Info note: entity-scoped tabs can only use EntityRule; global tabs can use both
|
|
82
|
+
- Read-only for a tab your customizations don't add
|
|
80
83
|
|
|
81
84
|
### 🌐 Web Resource Browser
|
|
82
85
|
- Virtualized list with type filter, custom-only toggle, search
|
|
@@ -97,7 +100,7 @@ reimagined with React 18, Fluent UI v9, and live Dataverse integration via
|
|
|
97
100
|
|
|
98
101
|
### 🔄 Publish Pipeline
|
|
99
102
|
- 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
|
|
100
|
-
- Pre-publish validation — blocking errors stop the import; warnings are logged
|
|
103
|
+
- Pre-publish validation — blocking errors stop the import; warnings are logged. It includes the schema rules the import enforces (empty rules, one-branch OrRules, function names, required values, numbers)
|
|
101
104
|
- Solution ZIP packaging with `behavior=2` (shell-only) root component — does **not** pull in all entity subcomponents
|
|
102
105
|
- Import → publish → re-import → publish, so Unified Interface picks up the change
|
|
103
106
|
- Waits and retries while the environment is busy with another import, publish or ribbon-metadata job
|
|
@@ -107,7 +110,7 @@ reimagined with React 18, Fluent UI v9, and live Dataverse integration via
|
|
|
107
110
|
- `Mscrm.OpenRecordItem` override support — emitted as a system-command override when the user defines a JS action on it
|
|
108
111
|
|
|
109
112
|
### 🧩 Solution Conflict Drawer
|
|
110
|
-
- Provenance
|
|
113
|
+
- Provenance: which managed solution adds a control, read from the managed ribbon layers (`ribbondiffs`) in one query (falls back to the namespace heuristic when unavailable)
|
|
111
114
|
- Banner in properties pane for managed-solution buttons
|
|
112
115
|
- Live solution-layer fetch in the drawer
|
|
113
116
|
|
|
@@ -148,11 +151,11 @@ started (if the tool's connection changes in Power Platform ToolBox, the operati
|
|
|
148
151
|
|
|
149
152
|
| Operation | When | What it does in Dataverse |
|
|
150
153
|
|---|---|---|
|
|
151
|
-
| **Publish** | **Publish All** in the top bar, or Ctrl+S | Builds a solution package for the selected unmanaged solution with the table's **complete** ribbon customization (what is in the solution's layer, plus your changes) and imports it **twice** (`ImportSolution` with `OverwriteUnmanagedCustomizations`), then publishes the table (`PublishXml`) and reads the layer back to verify it. It first publishes the table (`PublishXml`), so ribbon changes imported elsewhere but not yet published become part of the layer it reads, instead of being replaced; the publish at the end would publish them anyway. Just before each import it reads the layer again: if it changed since it was read (published from another session or tool), nothing is imported. The same for the application ribbon, when the solution contains the Application Ribbons component. **Also publish all customizations** adds `PublishAllXmlAsync`, which publishes everything unpublished in the environment. |
|
|
152
|
-
| **Upload a web resource** | Web resource browser → **Upload**; icon picker → **Upload SVG** | Creates a web resource named `<prefix>_/<folder>/<file name>` and adds it to the selected unmanaged solution (`AddSolutionComponent`). If one with that name exists you choose **Replace** (its content changes everywhere it is used), **Keep existing** or **Cancel**; a managed one can only be kept. |
|
|
154
|
+
| **Publish** | **Publish All** in the top bar, or Ctrl+S | Builds a solution package for the selected unmanaged solution with the table's **complete** ribbon customization (what is in the solution's layer, plus your changes) and imports it **twice** (`ImportSolution` with `OverwriteUnmanagedCustomizations`), then publishes the table (`PublishXml`) and reads the layer back to verify it. It first publishes the table (`PublishXml`), so ribbon changes imported elsewhere but not yet published become part of the layer it reads, instead of being replaced; the publish at the end would publish them anyway. Just before each import it reads the layer again: if it changed since it was read (published from another session or tool), nothing is imported. The same for the application ribbon, when the solution contains the Application Ribbons component. **Also publish all customizations** adds `PublishAllXmlAsync`, which publishes everything unpublished in the environment (anyone's), so it asks for its own confirmation. The **Publish comment** is written to the Console log. |
|
|
155
|
+
| **Upload a web resource** | Web resource browser → **Upload**; icon picker → **Upload SVG** | Creates a web resource named `<prefix>_/<folder>/<file name>` and adds it to the selected unmanaged solution (`AddSolutionComponent`). If one with that name exists you choose **Replace** (its content changes everywhere it is used), **Keep existing** or **Cancel**; a managed one can only be kept. It is published (`PublishXml`), so a replaced file is live at once. |
|
|
153
156
|
| **Pick a Fluent icon** | Icon picker → Fluent icons | Creates the SVG web resource `<prefix>_/icons/cbs/<icon>.svg` (an existing one is reused, never overwritten), adds it to the solution and publishes it (`PublishXml`). |
|
|
154
157
|
| **Pick an existing web resource as an icon** | Icon picker → Web resource | Adds the web resource to the selected unmanaged solution (`AddSolutionComponent`). |
|
|
155
|
-
| **Save a script** | Command editor → JavaScript action → **Save Web Resource** | Updates the content of the script you opened
|
|
158
|
+
| **Save a script** | Command editor → JavaScript action → **Save Web Resource** | Updates the content of the script you opened, adds it to the solution and publishes it (`PublishXml`), so users get the new version at once. The button shows the environment it saves to. |
|
|
156
159
|
|
|
157
160
|
Nothing else writes to Dataverse:
|
|
158
161
|
- **Unsaved changes** are kept as a draft in the browser storage of Power Platform ToolBox on your machine (`localStorage`), per environment, solution and table. A note of the last publish is kept for the session (`sessionStorage`).
|
|
@@ -162,7 +165,7 @@ Nothing else writes to Dataverse:
|
|
|
162
165
|
|
|
163
166
|
```
|
|
164
167
|
┌─────────────────────────────────────────────────────────────────┐
|
|
165
|
-
│ TopBar: Solution picker │ Ctrl+K │ Undo/Redo │
|
|
168
|
+
│ TopBar: Solution picker │ Ctrl+K │ Undo/Redo │ Help │ Publish │
|
|
166
169
|
│ SubHeader: Breadcrumb │ N changes since publish │ Preview … │
|
|
167
170
|
├──────────────────┬──────────────────────────────┬───────────────┤
|
|
168
171
|
│ Left Pane │ Ribbon Canvas │ Properties │
|
|
@@ -301,7 +304,7 @@ src/
|
|
|
301
304
|
dataverse/
|
|
302
305
|
ribbonService.ts — RetrieveEntityRibbon / RetrieveApplicationRibbon
|
|
303
306
|
publishService.ts — Layer merge, solution ZIP packaging, import, publish and verification
|
|
304
|
-
ribbonProvenanceService.ts —
|
|
307
|
+
ribbonProvenanceService.ts — managed-solution provenance from the managed ribbondiffs
|
|
305
308
|
conflictService.ts — fetchConflictLayersForElement
|
|
306
309
|
webResourceService.ts — loadWebResources / loadWebResourceByName / loadWebResourceContent
|
|
307
310
|
store/
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import{y as n,z as Pn}from"./fluent-icons-BqJj6rDi.js";import{p as je,m as Yn,g as Jn,n as Oe,o as Zn,q as Qn,r as es,t as ns,v as ss,x as Ke,y as ts,z as os,A as is,B as as,d as Se,D as rs,F as ls,G as cs,H as bn,I as ds,J as us,K as Xe,L as hs,M as ms,N as X,O as Nn,k as _e,P as Ie,Q as ps,R as bs,S as fn,T as fs,U as Ve,V as gs,W as gn,X as ys,Y as Dn,Z as K,_ as An,$ as ws,a0 as Fe,a1 as xs,a as Be,u as D,a2 as Cs,a3 as vs,a4 as js,a5 as yn}from"./index-Di2_5czx.js";import{a as A,R as se}from"./monaco-BIO15LCS.js";import{w as Rs,v as En}from"./xmlValidator-BkeotXuB.js";import{M as U,f as O,ae as Ss,b as F,B as Tn,F as Re,I as Ln,t as E,D as Is,i as $s,j as ks,k as Ps,l as Ns,a5 as Ds,ao as As,O as wn,an as Es,a1 as Ts,a6 as Ls,d as qe,ap as zs,m as Ms}from"./fluent-ui-QADO660t.js";import"./dnd-Cq8OftAq.js";import"./ruleKinds-B9hACGaq.js";import"./crmParameters-rgfZhhzN.js";let xn=0;async function He(e){const t=typeof window<"u"?window.toolboxAPI:void 0;xn++===0&&await Cn(()=>t?.preventClose?.());try{return await e()}finally{--xn===0&&await Cn(()=>t?.releasePreventClose?.())}}const Us=2e3;async function Cn(e){let t;try{await Promise.race([e(),new Promise(o=>t=setTimeout(o,Us))])}catch{}finally{clearTimeout(t)}}function me(e,t){const o=e.match(new RegExp(`\\s${t}="([^"]*)"`));return o?o[1]:null}function vn(e,t,o){const r=e.indexOf(o,t);return e.slice(t,r===-1?void 0:r).match(/<result\s[^>]*>/g)??[]}function jn(e){const t=e.find(o=>me(o,"result")!=="success");if(t){const o=me(t,"errorcode")??"",r=me(t,"errortext")??"unknown error";return{ok:!1,message:`Dataverse rejected the ribbon (${o}): ${r}`}}return null}function Os(e,t){if(!e)return{ok:!1,message:"The import job returned no result data, so the ribbon import could not be verified."};if(t.kind==="application"){const i=e.search(/<ribbon\s[^>]*processed="true"/);return i===-1?{ok:!1,message:"Import completed, but Dataverse did not process the application ribbon."}:jn(vn(e,i,"</ribbon>"))??{ok:!0,message:"Application ribbon imported."}}const o=new Set(t.names.filter(Boolean).map(i=>i.toLowerCase())),r=/<entityRibbon\s[^>]*>/g;let a;for(;a=r.exec(e);){const i=(me(a[0],"id")??"").toLowerCase();if(o.has(i))return me(a[0],"processed")!=="true"?{ok:!1,message:`Dataverse did not process the ribbon for ${i}.`}:jn(vn(e,a.index,"</entityRibbon>"))??{ok:!0,message:`Ribbon for ${i} imported.`}}return{ok:!1,message:`Import completed, but Dataverse did not apply a ribbon for ${t.names.join(" / ")} (no entityRibbon result in the import job). Nothing was changed.`}}const J=window.dataverseAPI;async function Xs(e,t,o){return await o("The import"),J.execute({operationName:"ImportSolution",operationType:"action",parameters:{CustomizationFile:e,ImportJobId:t,OverwriteUnmanagedCustomizations:!0,PublishWorkflows:!1}})}async function Vs(e,t){await t("Publishing");const o=e.map(r=>`<entity>${X(r)}</entity>`).join("");return J.execute({operationName:"PublishXml",operationType:"action",parameters:{ParameterXml:`<importexportxml><entities>${o}</entities></importexportxml>`}})}function Rn(e){const t=e.data?.progress;return!!(typeof t=="object"&&t!==null&&t.completed===!0||typeof e.progress=="number"&&e.progress>=100||typeof t=="number"&&t>=100||e.completedon||e.data?.completedon||e.statuscode===30)}function Bs(e){const t=e.titles.map(o=>`<Title languagecode="${o.languageCode}" description="${X(o.description)}"/>`).join("");return`<LocLabel Id="${X(e.id)}"><Titles>${t}</Titles></LocLabel>`}async function zn(e){const t=e.entity?.logicalName??"{!EntityLogicalName}",o={deletedContainerIds:e.plan.deletedContainerIds??[],movedButtons:e.plan.movedButtons??[],editedLocLabelIds:e.plan.editedLocLabelIds??[],editedTabIds:e.plan.editedTabIds??[],resequencedTabs:e.plan.resequencedTabs??[]},r=e.locLabels.filter(l=>o.editedLocLabelIds.includes(l.id)),a=e.languageCode??1033;for(const l of e.plan.renamedLabels??[]){const m=r.find(I=>I.id===l.id)??e.locLabels.find(I=>I.id===l.id),R=[{languageCode:a,description:l.text},...(m?.titles??[]).filter(I=>I.languageCode!==a)],y=r.findIndex(I=>I.id===l.id);y>=0?r[y]={id:l.id,titles:R}:r.push({id:l.id,titles:R})}let i=r.length?je(`<RibbonDiffXml><LocLabels>${r.map(Bs).join("")}</LocLabels></RibbonDiffXml>`):Yn();for(const l of e.locations){const m=Jn({current:l.current,baseline:l.baseline,commands:e.commands,enableRules:e.enableRules,displayRules:e.displayRules,locLabels:e.locLabels,entityLogicalName:t,location:l.current.location,languageCode:e.languageCode});i=Oe(i,je(m))}const d=[],u=[];for(const l of e.locations)for(const m of l.current.tabs){if(!o.editedTabIds.includes(m.id))continue;if(m.tabDisplayRules.length>0){const y=Zn(m,e.entity===null);y&&u.push(y)}const R=new Set(m.tabDisplayRules.map(y=>y.tabCommand));for(const y of l.baseline.tabs.find(I=>I.id===m.id)?.tabDisplayRules??[])R.has(y.tabCommand)||d.push(y.tabCommand)}u.length>0&&(i=Oe(i,je(`<RibbonDiffXml><RuleDefinitions><TabDisplayRules>${u.join("")}</TabDisplayRules></RuleDefinitions></RibbonDiffXml>`)));const f=[...e.commands.filter(l=>e.plan.editedCommandIds.includes(l.id)).map(Qn)],$={enable:e.enableRules.filter(l=>e.plan.editedEnableRuleIds.includes(l.id)).map(ns),display:e.displayRules.filter(l=>e.plan.editedDisplayRuleIds.includes(l.id)).map(es)};(f.length||$.enable.length||$.display.length)&&(i=Oe(i,je(`<RibbonDiffXml><CommandDefinitions>${f.join("")}</CommandDefinitions><RuleDefinitions><DisplayRules>${$.display.join("")}</DisplayRules><EnableRules>${$.enable.join("")}</EnableRules></RuleDefinitions></RibbonDiffXml>`)));const S=(l,m)=>{for(const R of Array.from(l.keys()))m.includes(R)||l.delete(R)},v=e.locations.flatMap(l=>l.current.tabs.filter(m=>!l.baseline.tabs.some(R=>R.id===m.id)).map(ss));S(i.commandDefinitions,[...e.plan.editedCommandIds,...v]),S(i.enableRules,e.plan.editedEnableRuleIds),S(i.displayRules,e.plan.editedDisplayRuleIds);const h=await Ke(e.entity?.logicalName??null),C=ts(h),b=e.plan.deletedButtonIds.filter(l=>!C.has(l));if(b.length>0)throw new Error(`Can't delete ${b.join(", ")}: not defined in this table's unmanaged customizations (it comes from a managed solution or the platform). Undo the delete and hide the button instead.`);const w=os(h),P=o.deletedContainerIds.filter(l=>!w.has(l));if(P.length>0)throw new Error(`Can't delete ${P.join(", ")}: not added by this table's unmanaged customizations (it comes from a managed solution or the platform). Undo the delete.`);const j=o.movedButtons.filter(l=>!C.has(l.id)).map(l=>l.id);if(j.length>0)throw new Error(`Can't move ${j.join(", ")}: not defined in this table's unmanaged customizations. Undo the move.`);const W=is(h),Z=(e.plan.uncustomisedCommandIds??[]).filter(l=>h.commandDefinitions.has(l)&&!as(l,W));if(Z.length>0)throw new Error(`Can't uncustomise ${Z.join(", ")}: it is your own command (your customizations' buttons use it and no other solution defines it), so removing it would leave those buttons without a command. Undo the uncustomise.`);const _=new Set;for(const l of e.locations)for(const m of l.current.tabs)for(const R of m.groups)for(const y of Se(R))for(const I of rs(y))_.add(I);const Q=e.plan.uncustomisedCommandIds??[],Y=new Set,ee=new Set;for(const l of e.commands)if(!(!_.has(l.id)||Q.includes(l.id))){l.enableRules.forEach(m=>Y.add(m)),l.displayRules.forEach(m=>ee.add(m));for(const m of l.rawRuleRefs??[]){const R=/^\s*<(EnableRule|DisplayRule)\b[^>]*?\sId="([^"]+)"/.exec(m.xml);R&&(R[1]==="EnableRule"?Y:ee).add(R[2])}}let k=ls(h,i,{retainedCommandIds:_,editedCommandIds:e.plan.editedCommandIds,editedEnableRuleIds:e.plan.editedEnableRuleIds,editedDisplayRuleIds:e.plan.editedDisplayRuleIds,deletedControlIds:e.plan.deletedButtonIds,unhiddenControlIds:e.plan.unhiddenButtonIds,deletedContainerIds:o.deletedContainerIds,movedControls:o.movedButtons,resequencedContainers:o.resequencedTabs,removedTabCommands:d,uncustomisedCommandIds:Q,retainedEnableRuleIds:Y,retainedDisplayRuleIds:ee});for(const l of e.plan.renamedLabels??[])l.newLabel&&(k=cs(k,l.containerId,`$LocLabels:${l.id}`));const c=new Set([...bn(h),...bn(k)].map(l=>l.id)),g={};for(const l of e.locations)for(const[m,R]of Object.entries(l.baseline.scalingByTab??{}))g[m]={maxSizes:R.maxSizes.filter(y=>!c.has(y.id)).map(y=>y.sequence),scales:R.scales.filter(y=>!c.has(y.id)).map(y=>y.sequence)};const L=ds(k,g);if(L.problems.length>0)throw new Error(`Not published: ${L.problems.join("; ")}.`);return k=L.parts,{scalingChanges:L.changes,xml:hs(k),parts:k,elementCount:Xe(k),preservedCount:Xe(h),changedCount:Xe(i),changedKeys:us(h,k),newKeys:Object.keys(k).flatMap(l=>Array.from(k[l].keys()).filter(m=>!h[l].has(m)).map(m=>`${l}:${m}`)),layerDefinitionIds:{commandIds:Array.from(h.commandDefinitions.keys()),enableRuleIds:Array.from(h.enableRules.keys()),displayRuleIds:Array.from(h.displayRules.keys())},layer:h}}function Mn(e,t){const o=[],r={},a={},i={},d={},u=new Set,f=new Set,$=new Set;for(const{current:h,baseline:C}of e.locations)for(const b of[C,h])for(const w of b.tabs)for(const P of w.groups)for(const j of Se(P))for(const W of j.menu?.sections??[])$.add(W.controlsId);for(const{current:h,baseline:C}of e.locations){Object.assign(i,C.templateLayouts);for(const b of C.tabs){o.push(b.id),b.label&&(d[b.id]={Title:b.label});for(const w of b.groups){u.add(w.id),o.push(w.id),w.label&&(d[w.id]={Title:w.label,Label:w.label});for(const P of Se(w))o.push(P.id),P.source&&(d[P.id]=P.source.attributes)}}for(const b of h.tabs)for(const w of b.groups)f.add(w.id);for(const b of[C,h])for(const w of b.tabs)for(const P of w.groups)P.template&&(a[P.id]=P.template)}const S=h=>u.has(h.groupId)&&!f.has(h.groupId);for(const{baseline:h}of e.locations)for(const[C,b]of Object.entries(h.scalingByTab??{}))r[C]={...b,maxSizes:b.maxSizes.filter(w=>!S(w)),scales:b.scales.filter(w=>!S(w))};const v=(h,C)=>{if(!h)return;const b=new Set(C??[]);return h.map(w=>w.id).filter(w=>!b.has(w))};return{changedKeys:t.changedKeys,newKeys:t.newKeys,commands:e.commands,known:{commandIds:v(e.baselineCommands,t.layerDefinitionIds?.commandIds),enableRuleIds:v(e.baselineEnableRules,t.layerDefinitionIds?.enableRuleIds),displayRuleIds:v(e.baselineDisplayRules,t.layerDefinitionIds?.displayRuleIds),elementIds:o},loadedAttributes:d,publisherPrefix:e.publisherPrefix,tabScaling:r,groupTemplates:a,templateLayouts:i,menuControlsIds:$,...e.entity?{places:{entityLogicalName:e.entity.logicalName,tabByCollection:Object.fromEntries(ms(e.locations.map(h=>h.baseline),e.entity.logicalName))}}:{}}}function qs(e,t){return new Promise((o,r)=>{const a=setTimeout(()=>r(new Error(`it didn't finish within ${Math.round(t/1e3)} s`)),t);e.then(i=>{clearTimeout(a),o(i)},i=>{clearTimeout(a),r(i)})})}async function Sn(e,t={}){const o=await zn(e),r=Mn(e,o),a=[],i=Rs(o.parts);if(t.lookupWebResources&&i.length>0)try{r.webResources=await t.lookupWebResources(i)}catch(d){a.push({severity:"warning",code:"webresource-lookup",message:`Couldn't check the ${i.length} referenced web resource(s): ${d instanceof Error?d.message:String(d)}`})}return{findings:[...En(o.parts,r),...a],xml:o.xml}}async function Fs(e,t,o={}){const r=o.attempts??3,a=o.contentKeys??[];let i={missing:[],unexpected:[],differing:[]};for(let u=0;u<r;u++){u>0&&await new Promise(f=>setTimeout(f,o.delayMs??5e3));try{i=Dn(t,await Ke(e),{contentKeys:a})}catch(f){return{ok:!1,missing:[],unexpected:[],differing:[],message:`Couldn't read the published ribbon layer to verify it (${f instanceof Error?f.message:String(f)}).`}}if(i.missing.length===0&&i.unexpected.length===0&&i.differing.length===0){const f=a.length;return{ok:!0,...i,message:"Verified: the published ribbon layer has every element that was imported"+(f>0?`, and the ${f} element(s) this publish changed have the imported content.`:".")}}}const d=u=>u.slice(0,5).join(", ")+(u.length>5?", …":"");return{ok:!1,...i,message:"The published ribbon layer doesn't match what was imported"+(i.missing.length?`; missing: ${d(i.missing)}`:"")+(i.unexpected.length?`; unexpected: ${d(i.unexpected)}`:"")+(i.differing.length?`; different content: ${d(i.differing)}`:"")+". Reload the table and check before publishing again."}}class We extends Error{constructor(t,o,r=!1){const a=r?"another solution import ran in the environment meanwhile, and what it changed may not be published yet":Un(o);super(r?`${t}'s ribbon customizations may have changed in Dataverse since you started publishing: ${a}. Importing could overwrite that change, so nothing was imported. Publish again: the table is published first, so the change is kept.`:`${t}'s ribbon customizations changed in Dataverse since you started publishing (${a}): other ribbon changes for it were published meanwhile, from another session, tool or solution import. Importing would overwrite that change, so nothing was imported. Reload the table to get the change, then publish again.`),this.scopeLabel=t,this.changes=o,this.importRan=r,this.name="RibbonLayerChangedError",this.summary=a}scopeLabel;changes;importRan;summary}function Un(e){const t=o=>o.slice(0,5).join(", ")+(o.length>5?", …":"");return[e.unexpected.length?`added: ${t(e.unexpected)}`:"",e.missing.length?`removed: ${t(e.missing)}`:"",e.differing.length?`changed: ${t(e.differing)}`:""].filter(Boolean).join("; ")}function Ws(e){const t=e instanceof Error?e.message:String(e);return/there is (?:another|a previous) \[(\w+)\] running/i.exec(t)?.[1]??null}const Ks=e=>Object.keys(e).flatMap(t=>Array.from(e[t].keys()).map(o=>`${t}:${o}`));function On(e,t){const o=[],r=[],a=[];for(const i of Object.keys(e)){for(const[d,u]of e[i]){const f=t[i].get(d);f===void 0?o.push(`${i}:${d}`):f!==u&&a.push(`${i}:${d}`)}for(const d of t[i].keys())e[i].has(d)||r.push(`${i}:${d}`)}return{missing:o,unexpected:r,differing:a}}const Xn=e=>e.missing.length+e.unexpected.length+e.differing.length>0;async function _s(e,t,o,r){const a=r.kind==="imported"?r.attempts??3:1;let i={missing:[],unexpected:[],differing:[]};for(let d=0;d<a;d++){d>0&&r.kind==="imported"&&await new Promise(f=>setTimeout(f,r.delayMs??5e3));const u=await Ke(e);if(i=r.kind==="read"?On(o,u):Dn(o,u,{contentKeys:Ks(o)}),!Xn(i))return}throw new We(t,i)}function In(e,t){return He(()=>Hs(e,t)).finally(()=>Nn({afterPublish:!0}))}async function Hs(e,t){const o=e.guard??_e(),r=Ie.getState(),a=e.entity?e.entity.logicalName:"Application ribbon",i={onProgress:t,...e.busy},d=e.entity===null;if(d&&!e.ribbonCustomizationIds?.length)throw new Error("The selected solution does not contain Application Ribbons. Add it in make.powerapps.com (Solutions → your solution → Add existing → Application Ribbons) and reload the solution.");const u=async(c="Publishing customizations…")=>{t?.(c),await gn("Publish",async()=>{d?(await o("Publishing"),await J.execute({operationName:"PublishXml",operationType:"action",parameters:{ParameterXml:"<importexportxml><ribbons><ribbon></ribbon></ribbons></importexportxml>"}})):await Vs([e.entity.logicalName],o)},i)},f=e.languageCode??await ps(),$=async c=>{t?.(`Reading existing ribbon customizations for ${a}…`),c&&r.logInfo("publish",`Reading unmanaged ribbon layer for ${a}.`);const g=await zn({...e,languageCode:f}),L=En(g.parts,Mn({...e,publisherPrefix:e.solution.publisherPrefix},g)),l=L.filter(m=>m.severity==="error");if(c||l.length>0){r.logInfo("publish",`RibbonDiffXml for ${a}: ${g.preservedCount} existing element(s) preserved, ${g.changedCount} element(s) from this session, ${g.elementCount} total.`);for(const m of g.scalingChanges)r.logWarn("publish",gs(m));for(const m of L)m.severity==="warning"&&r.logWarn("publish",m.message)}if(l.length>0){for(const m of l)r.logError("publish",m.message);throw new Error(`The pre-publish check found ${l.length} error(s), so nothing was imported: `+l.map(m=>m.message).join("; "))}return g},S=e.publishCustomizations!==!1?await $(!1):null;t?.("Checking that no other solution operation is running…"),await bs(i);const v=await Js(e.solution);let h=!1;const C=c=>(S&&!h&&c instanceof Error&&(c.message+=` (${a} was published first, so customizations imported elsewhere for it and not yet published are now published.)`),c);if(S){r.logInfo("publish",`Publishing ${a} first, so ribbon changes imported elsewhere but not yet published are kept.`);try{await u(`Publishing ${a} first (keeps changes imported elsewhere but not published)…`)}catch(c){throw c instanceof Error&&(c.message=`Publishing ${a} first failed, so nothing was imported: ${c.message}`),c}}let b;try{b=await $(!0)}catch(c){throw S&&c instanceof Error&&!c.message.startsWith("The pre-publish check")&&(c.message=`${a}'s customizations changed when it was published first (changes imported elsewhere and not yet published): ${c.message} Reload the table to get them, then publish again.`),C(c)}if(S){const c=On(S.layer,b.layer);Xn(c)&&r.logInfo("publish",`Publishing ${a} first published ribbon changes imported elsewhere (${Un(c)}); they are kept.`)}const w=e.entity?.schemaName||e.entity?.logicalName||"",P=d?fn({applicationRibbonDiffXml:b.xml}):fn({entities:[{schemaName:w,localizedName:e.entity?.displayName||w,ribbonDiffXml:b.xml}]}),j=d?[{type:50,schemaName:":RibbonDiffXml",behavior:0}]:[{type:1,schemaName:e.entity.logicalName,behavior:2}],W=et({...e.solution,languageCode:f,rootComponents:j,details:v});t?.("Packaging solution ZIP…");const Z=fs({"customizations.xml":Ve(P),"solution.xml":Ve(W),"[Content_Types].xml":Ve('<?xml version="1.0" encoding="utf-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="xml" ContentType="application/xml"/></Types>')});let _="";for(let c=0;c<Z.length;c++)_+=String.fromCharCode(Z[c]);const Q=btoa(_),Y=async(c,g)=>{t?.(`Importing ${a} ribbon${c}…`);const L=crypto.randomUUID();r.logInfo("publish",`Importing solution${c} (import job ${L})…`);let l=!1;await gn("Import",async()=>{if(await o("Importing"),l)throw new We(a,{missing:[],unexpected:[],differing:[]},!0);await _s(e.entity?.logicalName??null,a,g.layer,g.as);try{await Xs(Q,L,o)}catch(N){throw ys(N)&&Ws(N)?.toLowerCase()==="import"&&(l=!0),N}h=!0},i);const m=150,R=2e3;let y=await J.getImportJobStatus(L),I=0;for(;!Rn(y)&&I<m;)await new Promise(N=>setTimeout(N,R)),y=await J.getImportJobStatus(L),I++,I%10===0&&t?.(`Still importing… (${I*2}s elapsed)`);if(!Rn(y))throw new Error(`Solution import did not complete within ${m*R/1e3} seconds. Check Solutions › History for import job ${L}.`);t?.("Verifying the import…");const $e=await J.retrieve("importjob",L,["data"]),B=Os($e.data,d?{kind:"application"}:{kind:"entity",names:[w,e.entity.logicalName]});if(!B.ok)throw r.logError("publish",B.message),new Error(B.message);return r.logInfo("publish",B.message),L};let ee;try{ee=await Y("",{layer:b.layer,as:{kind:"read"}})}catch(c){throw C(c)}let k;if(e.publishCustomizations!==!1){await u();{r.logInfo("publish","Refreshing Unified Interface command-bar metadata (second import of the same package)…");try{await Y(" (refreshing Unified Interface metadata)",{layer:b.parts,as:{kind:"imported",delayMs:e.verifyDelayMs}}),await u()}catch(c){if(c instanceof We)k={ok:!1,...c.changes,message:`Your changes were imported and published, but ${a}'s ribbon customizations changed in Dataverse after that (${c.summary}): other changes were published meanwhile. They weren't imported a second time to refresh Unified Interface's command-bar metadata, since that would overwrite the other change, so new or changed buttons may not appear in the app until the table's ribbon is published again. Reload the table to see the other change.`},r.logWarn("publish",k.message);else throw new Error(`Your changes were imported and published, but refreshing Unified Interface's command-bar metadata failed (${c instanceof Error?c.message:String(c)}). New or changed buttons may not appear in the app until you publish again.`)}}k||(t?.("Verifying the published ribbon…"),k=await Fs(e.entity?.logicalName??null,b.parts,{delayMs:e.verifyDelayMs,contentKeys:b.changedKeys}),k.ok?r.logInfo("publish",k.message):r.logWarn("publish",k.message))}else r.logWarn("publish","Imported without publishing. New or changed buttons appear in Unified Interface only after you publish from Command Bar Studio (which re-imports once to refresh the command-bar metadata).");return t?.(e.publishCustomizations!==!1?`${a} ribbon published.`:`${a} ribbon imported (not published).`),{elementCount:b.elementCount,preservedCount:b.preservedCount,importJobId:ee,verification:k,parts:b.parts,changedKeys:b.changedKeys}}function Gs(e,t){return He(()=>Ys(e,t)).finally(()=>Nn({afterPublish:!0}))}async function Ys(e,t){const o=t?.guard??_e(),r=Ie.getState(),a=t?.pollIntervalMs??2e3,i=t?.timeoutMs??600*1e3;e?.("Starting asynchronous publish (PublishAllXmlAsync)..."),r.logInfo("publish","Calling PublishAllXmlAsync action."),await o("Publishing");const u=(await J.execute({operationName:"PublishAllXmlAsync",operationType:"action"}))?.AsyncOperationId;if(!u)throw new Error("PublishAllXmlAsync did not return AsyncOperationId.");r.logInfo("publish",`PublishAllXmlAsync job id: ${u}`),e?.("Async publish job started. Waiting for completion...");const f=Date.now();let $=0,S;for(;Date.now()-f<i;){$+=1;const v=await J.retrieve("asyncoperation",u,["asyncoperationid","statecode","statuscode","name","message","friendlymessage","completedon"]);if(S=v.statuscode,v.statecode===3||!!v.completedon){if(typeof v.statuscode=="number"&&v.statuscode!==30){const C=v.friendlymessage||v.message||`statuscode=${v.statuscode}`;throw new Error(`Asynchronous publish failed: ${C}`)}return e?.("Asynchronous publish completed successfully."),r.logInfo("publish",`PublishAllXmlAsync completed in ${$} poll(s).`),{asyncOperationId:u,statusCode:v.statuscode}}e?.(`Waiting for async publish job... (poll ${$})`),await new Promise(C=>setTimeout(C,a))}throw new Error(`Asynchronous publish timed out after ${Math.round(i/1e3)} seconds (last status: ${S??"unknown"}).`)}const Vn=[["AddressTypeCode","addresstypecode"],["City","city"],["County","county"],["Country","country"],["Fax","fax"],["FreightTermsCode","freighttermscode"],["Latitude","latitude"],["Line1","line1"],["Line2","line2"],["Line3","line3"],["Longitude","longitude"],["Name","name"],["PostalCode","postalcode"],["PostOfficeBox","postofficebox"],["PrimaryContactName","primarycontactname"],["ShippingMethodCode","shippingmethodcode"],["StateOrProvince","stateorprovince"],["Telephone1","telephone1"],["Telephone2","telephone2"],["Telephone3","telephone3"],["UPSZone","upszone"],["UTCOffset","utcoffset"],["UTCConversionTimeZoneCode","utcconversiontimezonecode"]];async function Js(e){let t;try{t=(await J.queryData(`solutions?$select=description&$expand=publisherid&$filter=uniquename eq '${e.uniqueName.replace(/'/g,"''")}'`)).value?.[0]}catch(d){throw new Error(`Couldn't read solution ${e.uniqueName} and its publisher (${d instanceof Error?d.message:String(d)}), so nothing was imported: an import without them would clear the publisher's details.`)}const o=t?.publisherid;if(!t||!o)throw new Error(`Couldn't read solution ${e.uniqueName} and its publisher, so nothing was imported: an import without them would clear the publisher's details.`);const r=o.customizationoptionvalueprefix;if(typeof r!="number")throw new Error(`Couldn't read the option-value prefix of publisher ${e.publisherUniqueName}, so nothing was imported: a wrong one would change the publisher.`);const a=d=>typeof d=="string"?d:"",i=[1,2].map(d=>Object.fromEntries(Vn.map(([,u])=>[u,o[`address${d}_${u}`]]).filter(([,u])=>u!=null&&u!==""))).map((d,u)=>({number:u+1,...d}));return{solutionDescription:a(t.description),publisherDescription:a(o.description),publisherEmail:a(o.emailaddress),publisherWebsite:a(o.supportingwebsiteurl),publisherOptionValuePrefix:r,publisherAddresses:i.filter(d=>Object.keys(d).length>1)}}const Zs=e=>X(e).replace(/\r/g,"
").replace(/\n/g,"
").replace(/\t/g,"	"),$n=(e,t)=>e?`<Descriptions>
|
|
2
|
+
<Description description="${Zs(e)}" languagecode="${t}"/>
|
|
3
|
+
</Descriptions>`:"<Descriptions/>",Qs=e=>e.length===0?"<Addresses/>":`<Addresses>${e.map(t=>`
|
|
4
|
+
<Address>
|
|
5
|
+
<AddressNumber>${t.number}</AddressNumber>${Vn.filter(([,o])=>o in t).map(([o,r])=>`
|
|
6
|
+
<${o}>${X(String(t[r]))}</${o}>`).join("")}
|
|
7
|
+
</Address>`).join("")}
|
|
8
|
+
</Addresses>`;function et(e){const t=e.publisherUniqueName?.trim()||"DefaultPublisher",o=e.publisherName?.trim()||t,r=e.publisherPrefix?.trim()||"new",a=e.languageCode||1033,i=e.friendlyName?.trim()||e.uniqueName,d=e.rootComponents.length===0?"<RootComponents/>":`<RootComponents>
|
|
9
|
+
${e.rootComponents.map(u=>{const f=[`type="${u.type}"`];return u.schemaName&&f.push(`schemaName="${X(u.schemaName)}"`),u.id&&f.push(`id="{${X(u.id)}}"`),f.push(`behavior="${u.behavior}"`),`<RootComponent ${f.join(" ")} />`}).join(`
|
|
10
|
+
`)}
|
|
11
|
+
</RootComponents>`;return`<?xml version="1.0" encoding="utf-8"?>
|
|
12
|
+
<ImportExportXml version="9.1.0.0" SolutionPackageVersion="9.1" languagecode="${a}" generatedBy="CommandBarStudio">
|
|
13
|
+
<SolutionManifest>
|
|
14
|
+
<UniqueName>${X(e.uniqueName)}</UniqueName>
|
|
15
|
+
<LocalizedNames>
|
|
16
|
+
<LocalizedName description="${X(i)}" languagecode="${a}"/>
|
|
17
|
+
</LocalizedNames>
|
|
18
|
+
${$n(e.details.solutionDescription,a)}
|
|
19
|
+
<Version>${X(e.version)}</Version>
|
|
20
|
+
<Managed>0</Managed>
|
|
21
|
+
<Publisher>
|
|
22
|
+
<UniqueName>${X(t)}</UniqueName>
|
|
23
|
+
<LocalizedNames>
|
|
24
|
+
<LocalizedName description="${X(o)}" languagecode="${a}"/>
|
|
25
|
+
</LocalizedNames>
|
|
26
|
+
${$n(e.details.publisherDescription,a)}
|
|
27
|
+
<EMailAddress>${X(e.details.publisherEmail)}</EMailAddress>
|
|
28
|
+
<SupportingWebsiteUrl>${X(e.details.publisherWebsite)}</SupportingWebsiteUrl>
|
|
29
|
+
<CustomizationPrefix>${X(r)}</CustomizationPrefix>
|
|
30
|
+
<CustomizationOptionValuePrefix>${e.details.publisherOptionValuePrefix}</CustomizationOptionValuePrefix>
|
|
31
|
+
${Qs(e.details.publisherAddresses)}
|
|
32
|
+
</Publisher>
|
|
33
|
+
${d}
|
|
34
|
+
<MissingDependencies/>
|
|
35
|
+
</SolutionManifest>
|
|
36
|
+
</ImportExportXml>`}const nt=Pn({root:{display:"flex",flexDirection:"column",gap:E.spacingVerticalS},box:{display:"grid",gridTemplateColumns:"auto 1fr",columnGap:E.spacingHorizontalM,rowGap:E.spacingVerticalXS,padding:`${E.spacingVerticalS} ${E.spacingHorizontalM}`,backgroundColor:E.colorNeutralBackground3,borderRadius:E.borderRadiusMedium},label:{color:E.colorNeutralForeground3},value:{display:"flex",flexWrap:"wrap",alignItems:"center",columnGap:E.spacingHorizontalS,minWidth:0},secondary:{color:E.colorNeutralForeground3,wordBreak:"break-all"}});function st({rows:e,confirmWord:t,onConfirmationChange:o,disabled:r}){const a=nt(),[i,d]=A.useState({status:"loading"}),[u,f]=A.useState(""),$=K(j=>j.environment),S=K(j=>j.connectionUrl),v=K(j=>j.connectionVersion);A.useEffect(()=>{let j=!0;return d({status:"loading"}),An().then(W=>j&&d({status:"ready",connection:W})).catch(()=>j&&d({status:"ready",connection:null})),()=>{j=!1}},[$,S,v]);const h=i.status==="ready"?i.connection:null,C=ws(h?.environment),b=i.status==="ready"&&Fe(h?.environment),w=i.status==="ready"&&(!b||xs(u,t)),P=h?.environment;return A.useEffect(()=>{o(w,P)},[w,P,o]),n.jsxs("div",{className:a.root,children:[C&&n.jsx(U,{intent:"warning",children:n.jsxs(O,{children:[n.jsx(Ss,{children:"Production environment"}),"This changes the live environment."]})}),n.jsxs("div",{className:a.box,children:[n.jsx(F,{className:a.label,children:"Environment"}),n.jsx("div",{className:a.value,children:i.status==="loading"?n.jsx(F,{children:"Reading the connection…"}):h?n.jsxs(n.Fragment,{children:[n.jsx(F,{weight:"semibold",children:h.name}),h.environment&&n.jsx(Tn,{size:"small",appearance:C?"filled":"tint",color:C?"warning":"informative",children:h.environment}),n.jsx(F,{size:200,className:a.secondary,children:h.url})]}):n.jsx(F,{children:"Unknown (the tool has no connection)"})}),e.map(j=>n.jsxs("div",{style:{display:"contents"},children:[n.jsx(F,{className:a.label,children:j.label}),n.jsx("div",{className:a.value,children:j.value})]},j.label))]}),b&&n.jsx(Re,{label:n.jsxs(n.Fragment,{children:["Type ",n.jsx("strong",{children:t})," to confirm"]}),hint:"Required on a Production environment.",children:n.jsx(Ln,{value:u,onChange:(j,W)=>f(W.value),disabled:r,autoComplete:"off","aria-label":`Type ${t} to confirm`})})]})}const tt=Pn({content:{display:"flex",flexDirection:"column",gap:E.spacingVerticalM},progressList:{display:"flex",flexDirection:"column",gap:E.spacingVerticalXS},progressRow:{display:"flex",alignItems:"center",gap:E.spacingHorizontalS}}),kn=({findings:e,showScope:t})=>n.jsx("ul",{style:{margin:`${E.spacingVerticalXS} 0 0`,paddingLeft:20},children:e.map((o,r)=>n.jsxs("li",{children:[t&&n.jsxs("strong",{children:[o.scope,": "]}),o.message]},r))}),de=["HomepageGrid","SubGrid","Form"],ot=3e4,mt=()=>{const e=tt(),t=Be(s=>s.bulkPublishOpen),o=Be(s=>s.closeBulkPublish),r=Be(s=>s.openXmlDrawer),a=D(s=>s.ribbons),i=D(s=>s.baseline),d=D(s=>s.commands),u=D(s=>s.displayRules),f=D(s=>s.enableRules),$=D(s=>s.baselineCommands),S=D(s=>s.baselineEnableRules),v=D(s=>s.baselineDisplayRules),h=D(s=>s.locLabels),C=D(s=>s.baselineLocLabels),b=D(s=>s.layers),w=D(s=>s.pendingUnhides),P=D(s=>s.orphans),j=D(s=>s.pendingOrphanDeletes),W=D(s=>s.customisedCommandIds),Z=D(s=>s.uncustomisedCommandIds),_=D(s=>s.baseLanguageCode),Q=D(s=>s.baseLanguageUnknown),Y=D(s=>s.setLayerSnapshot),ee=D(s=>s.markPublished),k=D(s=>s.mutationsSincePublish),c=K(s=>s.activeSolution),g=K(s=>s.activeEntity),L=K(s=>s.connectionUrl),l=K(s=>s.publisherPrefix),m=K(s=>s.solutionRibbonComponentIds),R=Array.isArray(m)&&m.length>0,[y,I]=A.useState(!1),[$e,B]=A.useState({}),[N,ke]=A.useState(!1),[pe,Bn]=A.useState(!1),[ae,Ge]=A.useState("targeted"),[Ye,Pe]=A.useState(!1),Je=ae==="allAsync"&&!Ye,Ze=K(s=>s.connectionName||s.connectionUrl||"this environment"),[Qe,Ne]=A.useState({confirmed:!1}),qn=se.useCallback((s,p)=>Ne({confirmed:s,environment:p}),[]),en=Qe.confirmed,[Fn,nn]=A.useState(0),[sn,re]=A.useState(""),[De,tn]=A.useState(""),[be,on]=A.useState(c?.version??"1.0.0.0"),[fe,ge]=A.useState([]),[Wn,an]=A.useState([]),[Kn,ye]=A.useState([]),rn=se.useMemo(()=>{const p=(be||c?.version||"1.0.0.0").split(".").map(q=>Number.parseInt(q,10)||0);for(;p.length<4;)p.push(0);return p[3]+=1,p.slice(0,4).join(".")},[c?.version,be]);se.useEffect(()=>{on(c?.version??"1.0.0.0")},[c?.version]);const we=se.useCallback((s,p,q)=>Cs({locations:s.map(M=>({current:a[M],baseline:i[M]})),excludeCommandIds:q,locLabels:h,baselineLocLabels:C,layer:b[p]??void 0,unhiddenLayerHideIds:w[p],deletedOrphans:P[p].filter(M=>j[p].includes(M.id)),customisedCommandIds:W,uncustomisedCommandIds:Z[p],commands:d,baselineCommands:$,enableRules:f,baselineEnableRules:S,displayRules:u,baselineDisplayRules:v}),[a,i,d,$,f,S,u,v,h,C,b,w,P,j,W,Z]),H=se.useMemo(()=>g?we(de,"entity"):null,[g,we]),ne=se.useMemo(()=>{const s=new Set;if(g)for(const p of de)for(const q of a[p].tabs)for(const M of q.groups)for(const z of Se(M))z.commandId&&s.add(z.commandId);return we(["Application"],"application",s)},[we,g,a]),le=ne.hasChanges&&R,ue=[...H?.unsupported??[],...ne.unsupported],ln=[...H?.warnings??[],...ne.warnings],G=[...g&&H?.hasChanges?[g.logicalName]:[],...le?["Application ribbon"]:[]],_n=g&&H?.hasChanges?g.logicalName:"application ribbon",[Ae,xe]=A.useState("idle"),[Ee,Te]=A.useState([]),[Hn,Le]=A.useState(""),te=t&&!!c&&ue.length===0&&G.length>0,cn=c?.id,dn=c?.publisherPrefix||l,un=se.useCallback(async()=>{const s={commands:d,enableRules:f,displayRules:u,locLabels:h,baselineCommands:$,baselineEnableRules:S,baselineDisplayRules:v,publisherPrefix:dn,languageCode:_},p={lookupWebResources:M=>vs(M,cn)},q=[];if(g&&H?.hasChanges){const{findings:M}=await Sn({...s,entity:{logicalName:g.logicalName,schemaName:g.schemaName},locations:de.map(z=>({current:a[z],baseline:i[z]})),plan:H},p);q.push(...M.map(z=>({...z,scope:g.logicalName})))}if(le){const{findings:M}=await Sn({...s,entity:null,locations:[{current:a.Application,baseline:i.Application}],plan:ne},p);q.push(...M.map(z=>({...z,scope:"Application ribbon"})))}return q},[g,H,ne,le,a,i,d,f,u,h,$,S,v,dn,cn,_]);se.useEffect(()=>{if(!te||y||N)return;let s=!1;return xe("running"),Le(""),qs(un(),ot).then(p=>{s||(Te(p),xe("done"))}).catch(p=>{s||(Te([]),Le(p instanceof Error?p.message:String(p)),xe("failed"))}),()=>{s=!0}},[te,y,N,un]);const ce=Ee.filter(s=>s.severity==="error"),Ce=Ee.filter(s=>s.severity==="warning"),hn=new Set(Ee.map(s=>s.scope)).size>1,mn=te&&(Ae==="running"||ce.length>0),Gn=()=>He(async()=>{if(!c||ue.length>0||G.length===0||mn||!en||Q||Je)return;I(!0);const s=_e(),p=K.getState().connectionVersion,q=()=>K.getState().connectionVersion!==p,M=await An().catch(()=>null);if(q()){I(!1);return}if(Fe(M?.environment)&&!Fe(Qe.environment)){Ne({confirmed:!1}),nn(x=>x+1),I(!1),Ie.getState().logWarn("publish",`${M?.name??"The connection"} is a Production environment now: type the confirmation to publish.`);return}re(""),ge([]),ye([]),an(G),B(Object.fromEntries(G.map(x=>[x,"pending"])));let z=!1;const oe=[],Me={},ie=[],ve=[],Ue=Ie.getState(),he=x=>x instanceof Error?x.message:String(x);De.trim()&&Ue.logInfo("publish",`Publish comment: ${De.trim()}`);const pn={commands:d,enableRules:f,displayRules:u,locLabels:h,baselineCommands:$,baselineEnableRules:S,baselineDisplayRules:v,publishCustomizations:!0,languageCode:_,solution:{uniqueName:c.uniqueName,friendlyName:c.friendlyName,version:pe?rn:be,publisherUniqueName:c.publisherUniqueName,publisherName:c.publisherName,publisherPrefix:c.publisherPrefix||l,publisherOptionValuePrefix:c.publisherOptionValuePrefix}};if(g&&H?.hasChanges){const x=g.logicalName;B(T=>({...T,[x]:"publishing"}));try{const T=await In({...pn,guard:s,entity:{logicalName:g.logicalName,schemaName:g.schemaName,displayName:g.displayName},locations:de.map(V=>({current:a[V],baseline:i[V]})),plan:H},V=>re(V));oe.push(...de),Me.entity=T.parts,js(L,g.logicalName,T.changedKeys),T.verification&&ve.push({scope:x,result:T.verification}),B(V=>({...V,[x]:"success"}))}catch(T){z=!0,ie.push({scope:x,message:he(T)}),Ue.logError("publish",`Ribbon publish failed (${x}): ${he(T)}`),B(V=>({...V,[x]:"error"}))}}else g&&oe.push(...de);if(le&&!q()){const x="Application ribbon";B(T=>({...T,[x]:"publishing"}));try{const T=await In({...pn,guard:s,entity:null,locations:[{current:a.Application,baseline:i.Application}],ribbonCustomizationIds:m??[],plan:ne},V=>re(V));oe.push("Application"),Me.application=T.parts,T.verification&&ve.push({scope:x,result:T.verification}),B(V=>({...V,[x]:"success"}))}catch(T){z=!0,ie.push({scope:x,message:he(T)}),Ue.logError("publish",`Application ribbon publish failed: ${he(T)}`),B(V=>({...V,[x]:"error"}))}}else ne.hasChanges||oe.push("Application");if(!z&&ae==="allAsync")try{await Gs(x=>re(x),{guard:s})}catch(x){z=!0,ie.push({scope:"Publish all customizations",message:he(x)})}if(q()){le&&!ie.some(x=>x.scope==="Application ribbon")&&ie.push({scope:"Application ribbon",message:"Not published: the tool's connection changed during the publish."}),ye(ve),ge(ie),ke(!1),I(!1);return}if(ee(oe,Me),g&&oe.includes("HomepageGrid")&&H?.hasChanges){const x=await yn(g.logicalName);x&&Y("entity",x)}if(oe.includes("Application")&&le){const x=await yn(null);x&&Y("application",x)}z||re("Done."),ye(ve),ge(ie),ke(!z),I(!1)}),ze=()=>{I(!1),B({}),an([]),ke(!1),re(""),ge([]),ye([]),xe("idle"),Te([]),Le(""),Ne({confirmed:!1}),Ge("targeted"),Pe(!1),tn(""),nn(s=>s+1),o()};return n.jsx(Is,{open:t,onOpenChange:()=>!y&&ze(),children:n.jsxs($s,{style:{maxWidth:"640px",maxHeight:"92vh",display:"flex",flexDirection:"column"},children:[n.jsx(ks,{children:"Publish to Dataverse"}),n.jsxs(Ps,{style:{flex:1,minHeight:0,overflow:"hidden"},children:[n.jsx(Ns,{style:{overflowY:"auto",maxHeight:"100%"},children:n.jsxs("div",{className:e.content,children:[!c&&n.jsx(U,{intent:"warning",children:n.jsx(O,{children:"No solution selected. Please select a solution first."})}),Q&&!N&&n.jsx(U,{intent:"error",layout:"multiline",children:n.jsx(O,{children:"The organization's base language couldn't be read when the table loaded, so labels are shown in English. Reload the table before publishing: publishing now could write English texts as the base language's titles."})}),ue.length>0&&!N&&n.jsx(U,{intent:"error",layout:"multiline",children:n.jsxs(O,{children:[n.jsx("strong",{children:"These changes can't be published yet. Undo them to publish:"}),n.jsx("ul",{style:{margin:`${E.spacingVerticalXS} 0 0`,paddingLeft:20},children:ue.map(s=>n.jsx("li",{children:s},s))})]})}),ln.length>0&&!N&&n.jsx(U,{intent:"info",layout:"multiline",children:n.jsxs(O,{children:[n.jsx("strong",{children:"Good to know:"}),n.jsx("ul",{style:{margin:`${E.spacingVerticalXS} 0 0`,paddingLeft:20},children:ln.map(s=>n.jsx("li",{children:s},s))})]})}),te&&!N&&!y&&Ae==="running"&&n.jsxs("div",{className:e.progressRow,children:[n.jsx(Ds,{size:"tiny"}),n.jsx(F,{children:"Checking the ribbon customizations before publishing…"})]}),te&&!N&&Ae==="failed"&&n.jsx(U,{intent:"warning",layout:"multiline",children:n.jsxs(O,{children:["The pre-publish check couldn't complete (",Hn,"). Publishing runs the check again before anything is imported."]})}),te&&!N&&ce.length>0&&n.jsx(U,{intent:"error",layout:"multiline",children:n.jsxs(O,{children:[n.jsxs("strong",{children:["Publishing is blocked: the pre-publish check found ",ce.length," error",ce.length!==1?"s":"",". Fix"," ",ce.length!==1?"them":"it",", then reopen this dialog:"]}),n.jsx(kn,{findings:ce,showScope:hn})]})}),te&&!N&&Ce.length>0&&n.jsx(U,{intent:"warning",layout:"multiline",children:n.jsxs(O,{children:[n.jsxs("strong",{children:[Ce.length," warning",Ce.length!==1?"s":""," — you can still publish:"]}),n.jsx(kn,{findings:Ce,showScope:hn})]})}),ne.hasChanges&&!R&&!N&&n.jsx(U,{intent:"warning",layout:"multiline",children:n.jsxs(O,{children:["You changed the Application ribbon, but this solution doesn't include the Application Ribbons component, so those changes stay pending. In make.powerapps.com open the solution, choose ",n.jsx("strong",{children:"Add existing → Application Ribbons"}),", then reload here."]})}),n.jsx(U,{intent:"info",layout:"multiline",children:n.jsxs(O,{children:["Publishing writes this table's complete ribbon customization: what is already published, plus your changes. Ribbon changes on this table that were imported elsewhere but ",n.jsx("strong",{children:"not published"})," are published first, so they are kept. Publishing takes about 1–4 minutes (the ribbon is imported twice so Unified Interface picks up the change). Users may need to refresh the page, or navigate away and back, to see the update. You have ",n.jsx("strong",{children:k})," change(s) since the last publish."]})}),fe.length>0&&n.jsx(U,{intent:"error",layout:"multiline",children:n.jsxs(O,{children:[n.jsxs("strong",{children:["Publish failed (",fe.length," error",fe.length!==1?"s":"",")"]}),n.jsx("ul",{style:{margin:`${E.spacingVerticalXS} 0 0`,paddingLeft:20},children:fe.map((s,p)=>n.jsxs("li",{style:{fontFamily:E.fontFamilyMonospace},children:[n.jsxs("strong",{children:[s.scope,":"]})," ",s.message]},p))}),n.jsxs("div",{style:{marginTop:E.spacingVerticalS},children:["Full details are in the ",n.jsx("strong",{children:"Console"})," tab of the bottom panel."]})]})}),!y&&!N&&n.jsxs(n.Fragment,{children:[n.jsx(st,{rows:[{label:"Solution",value:c?n.jsx(F,{weight:"semibold",children:c.friendlyName||c.uniqueName}):n.jsx(F,{children:"None selected"})},{label:"Will publish",value:n.jsx(F,{weight:"semibold",children:G.length>0?G.join(", "):"nothing (there are no changes to publish)"})},{label:"Writes",value:n.jsxs(F,{children:["Publishes ",G.length===1?"it":"them"," first (keeps changes imported elsewhere but not published),"," ",G.length===1?"imports its":"imports each one's"," ribbon customization twice, replacing what the solution's layer has, then publishes"," ",ae==="allAsync"?"every unpublished customization in the environment":G.length===1?"it":"them","."]})}],confirmWord:_n,onConfirmationChange:qn},Fn),n.jsx(Re,{label:"Afterwards",children:n.jsxs(As,{value:ae==="targeted"?"Publish only these ribbons":"Also publish all customizations (PublishAllXmlAsync)",selectedOptions:[ae],onOptionSelect:(s,p)=>{(p.optionValue==="targeted"||p.optionValue==="allAsync")&&(Ge(p.optionValue),Pe(!1))},children:[n.jsx(wn,{value:"targeted",children:"Publish only these ribbons"}),n.jsx(wn,{value:"allAsync",children:"Also publish all customizations (PublishAllXmlAsync)"})]})}),ae==="allAsync"&&n.jsx(U,{intent:"warning",layout:"multiline",children:n.jsxs(O,{children:[n.jsxs(F,{block:!0,children:["This publishes ",n.jsx("strong",{children:"every"})," unpublished customization in"," ",n.jsx("strong",{children:Ze}),", not only these ribbons: forms, views, tables and other people's work in progress included."]}),n.jsx(Es,{checked:Ye,onChange:(s,p)=>Pe(p.checked===!0),label:`Publish all unpublished customizations in ${Ze}`})]})}),n.jsx(Re,{label:"Solution version",children:n.jsx(Ln,{size:"small",value:pe?rn:be,onChange:(s,p)=>on(p.value),disabled:pe})}),n.jsx(Ts,{label:"Increment solution version",checked:pe,onChange:(s,p)=>Bn(p.checked===!0)}),n.jsx(Re,{label:"Publish comment",hint:"Written to the Console log with this publish; it isn't stored in Dataverse.",children:n.jsx(Ls,{resize:"vertical",value:De,onChange:(s,p)=>tn(p.value)})}),n.jsx(qe,{appearance:"subtle",onClick:()=>{r(),ze()},children:"View RibbonDiffXml"})]}),N&&n.jsx(U,{intent:"info",layout:"multiline",children:n.jsx(O,{children:"Unified Interface caches command bars by metadata version, and publishing a ribbon doesn't always change that version. If the app still shows the old command bar, hard-refresh the page (Ctrl+F5) or sign out and back in; it can take several minutes to appear for users who already had the table open."})}),Kn.map(s=>n.jsx(U,{intent:s.result.ok?"success":"warning",layout:"multiline",children:n.jsxs(O,{children:[n.jsxs("strong",{children:[s.scope,":"]})," ",s.result.message]})},s.scope)),(y||N)&&n.jsxs("div",{className:e.progressList,children:[Wn.map(s=>{const p=$e[s]??"pending";return n.jsxs("div",{className:e.progressRow,children:[n.jsx(Tn,{appearance:"filled",color:p==="success"?"success":p==="error"?"danger":p==="publishing"?"informative":"subtle",size:"small",children:p}),n.jsx(F,{children:s}),p==="publishing"&&n.jsx(zs,{thickness:"large",style:{flex:1}})]},s)}),sn&&n.jsx(U,{intent:"info",children:n.jsx(O,{children:sn})})]})]})}),n.jsxs(Ms,{children:[n.jsx(qe,{appearance:"secondary",onClick:ze,disabled:y,children:N?"Close":"Cancel"}),!N&&n.jsx(qe,{appearance:"primary",onClick:()=>{Gn()},disabled:y||!c||G.length===0||ue.length>0||mn||!en||Q||Je,children:y?"Publishing...":"Publish"})]})]})]})})};export{mt as BulkPublishModal,mt as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{y as e,h as N,z as Be,cq as ue}from"./fluent-icons-BqJj6rDi.js";import{a as S,u as b,i as Pe,b as We,d as $e,C as Le,w as Te,E as Oe,e as Ve,f as He,h as Ue,j as _e,k as Xe}from"./index-Di2_5czx.js";import{a as m}from"./monaco-BIO15LCS.js";import{u as Je,C as me}from"./useStableKeys-p2OkzZWO.js";import{M as qe}from"./MonacoEditor-uzfuRCpJ.js";import{ai as Ke,aj as Ge,ak as Ye,b as d,t as n,d as u,al as Qe,ac as Ze,ad as z,a7 as X,F as j,S as J,B as I,M as C,f as w,I as $}from"./fluent-ui-QADO660t.js";import"./dnd-Cq8OftAq.js";import"./crmParameters-rgfZhhzN.js";const es=Be({drawer:{width:"640px",maxWidth:"90vw"},body:{display:"flex",flexDirection:"column",gap:n.spacingVerticalS,padding:n.spacingVerticalM,overflowY:"auto"},actionCard:{border:`1px solid ${n.colorNeutralStroke2}`,borderRadius:n.borderRadiusMedium,padding:n.spacingVerticalM,display:"flex",flexDirection:"column",gap:n.spacingVerticalS},actionHeader:{display:"flex",alignItems:"center",justifyContent:"space-between"},row:{display:"flex",gap:n.spacingHorizontalS,alignItems:"flex-end"},ruleRow:{display:"inline-flex",alignItems:"center",gap:n.spacingHorizontalXS},ruleRemoveBtn:{minWidth:"20px",width:"20px",height:"20px"},ruleChip:{cursor:"pointer"},rulePills:{display:"flex",flexWrap:"wrap",gap:n.spacingHorizontalXS},versionsList:{display:"flex",flexDirection:"column",gap:n.spacingVerticalXS},versionRow:{border:`1px solid ${n.colorNeutralStroke2}`,borderRadius:n.borderRadiusMedium,padding:n.spacingVerticalXS,display:"flex",justifyContent:"space-between",gap:n.spacingHorizontalS},versionMeta:{fontSize:n.fontSizeBase100,color:n.colorNeutralForeground3},securityNote:{fontSize:n.fontSizeBase100},readOnly:{border:"none",margin:0,padding:0,minWidth:0,display:"flex",flexDirection:"column",gap:n.spacingVerticalS},codePanel:{border:`1px solid ${n.colorNeutralStroke2}`,borderRadius:n.borderRadiusMedium,padding:n.spacingVerticalS,display:"flex",flexDirection:"column",gap:n.spacingVerticalS}}),ss=[],ds=()=>{const l=es(),L=S(s=>s.commandEditorOpen),k=S(s=>s.commandEditorId),M=S(s=>s.closeCommandEditor),pe=S(s=>s.openCommandEditor),q=S(s=>s.openRuleEditor),he=S(s=>s.openWebResourceBrowser),T=b(s=>s.commands),be=b(s=>s.upsertCommand),xe=b(s=>s.renameCommandId),K=b(s=>s.ribbons),G=b(s=>s.displayRules),Y=b(s=>s.enableRules),je=b(s=>s.past),fe=b(s=>s.upsertEnableRule),ge=b(s=>s.upsertDisplayRule),[f,ve]=m.useState("actions"),[p,Q]=m.useState(""),[ye,Z]=m.useState(""),[Ce,ee]=m.useState(""),[se,A]=m.useState(null),[E,ae]=m.useState(!1),[ne,D]=m.useState(!1),[O,g]=m.useState(null),[re,V]=m.useState(null),[le,F]=m.useState(""),[we,B]=m.useState(!1),[R,ie]=m.useState(null),r=T.find(s=>s.id===k),v=Je(`actions:${r?.id??""}`,r?.actions??ss),Re=Pe(We(r?.id)),Ne=s=>{const a=s.trim();return a.startsWith("$webresource:")?a.slice(13).trim():a},Se=s=>{if(!s)return"";const a=Uint8Array.from(atob(s),o=>o.charCodeAt(0));return new TextDecoder().decode(a)},ze=s=>{const a=new TextEncoder().encode(s);let o="";for(let i=0;i<a.length;i+=1)o+=String.fromCharCode(a[i]);return btoa(o)},ke=s=>{const a=s.split(".").pop()?.toLowerCase()??"";return{html:1,css:2,js:3,xml:4,png:5,jpg:6,gif:7,xsl:9,ico:10,svg:11,resx:12}[a]??3};m.useEffect(()=>{r&&Q(r.id)},[r]),m.useEffect(()=>{L&&k&&!r&&M()},[L,k,r,M]);const P=m.useMemo(()=>r?p.trim()?/\s/.test(p)?"Command ID cannot contain whitespace.":p!==r.id&&T.some(s=>s.id.toLowerCase()===p.toLowerCase())?"A command with this ID already exists.":"":"Command ID is required.":"",[p,r,T]),H=m.useMemo(()=>Object.entries(K).flatMap(([s,a])=>a.tabs.flatMap(o=>o.groups.flatMap(i=>$e(i).filter(c=>c.commandId===k).map(c=>({location:s,tab:o,group:i,button:c}))))),[K,k]),oe=H.length;if(!r)return null;const te=async(s,a)=>{const o=r.actions[s];if(!o||o.kind!=="javascript")return;const i=Ne(o.library);if(!i){g("JavaScript library must be a valid $webresource path."),A(s),ae(a),F(""),ie(null);return}A(s),ae(a),D(!0),g(null),V(null),B(!1),ie(i);try{const c=await He(i);if(!c){F(""),g(`Web resource not found: ${i}`);return}const t=await Ue(c.id);F(Se(t))}catch(c){g(c instanceof Error?c.message:"Failed to load web resource content."),F("")}finally{D(!1)}},Ee=async()=>{if(!E||se===null||!R)return;const s=Xe();D(!0),g(null),V(null);try{const a=await _e(R,ze(le),ke(R),{displayName:R.split("/").pop(),guard:s});V([a.solutionWarning,a.publishWarning].filter(Boolean).join(" ")||null),B(!1)}catch(a){g(a instanceof Error?a.message:"Failed to save web resource content.")}finally{D(!1)}},y=s=>{be({...r,...s})},x=(s,a)=>{const o=[...r.actions];o[s]=a,y({actions:o}),v.update()},W=s=>{y({actions:r.actions.filter((a,o)=>o!==s)}),v.remove(s),A(a=>a===null||a===s?null:a>s?a-1:a)},U=s=>{const a=s==="javascript"?{kind:"javascript",library:"",functionName:"",params:[]}:{kind:"url",address:"https://",params:[]};y({actions:[...r.actions,a]}),v.add()},Fe=(s,a)=>{const o=se===a;if(s.kind==="javascript")return e.jsxs("div",{className:l.actionCard,children:[e.jsxs("div",{className:l.actionHeader,children:[e.jsx(I,{appearance:"filled",color:"success",size:"small",children:"JavaScript"}),e.jsx(u,{appearance:"subtle",size:"small",icon:e.jsx(N,{}),onClick:()=>W(a),"aria-label":"Remove action"})]}),e.jsx(j,{label:"Library (Web Resource)",children:e.jsxs("div",{style:{display:"flex",gap:"4px",alignItems:"center"},children:[e.jsx($,{style:{flex:1},value:s.library,onChange:(i,c)=>x(a,{...s,library:c.value.replace(/^(\s*\$webresource:\s*)+/i,"$webresource:")}),placeholder:"$webresource:publisher_/scripts/myfile.js"}),e.jsx(u,{appearance:"subtle",size:"small",onClick:()=>{Te.set(i=>{x(a,{...s,library:`$webresource:${i.name}`})}),he()},children:"Browse…"})]})}),e.jsx(j,{label:"Function name",children:e.jsx($,{value:s.functionName,onChange:(i,c)=>x(a,{...s,functionName:c.value}),placeholder:"MyNamespace.myFunction"})}),e.jsx(C,{intent:"info",children:e.jsx(w,{children:"Read-only function preview and Monaco editing are available in the web resource browser integration path."})}),e.jsx(X,{children:"Parameters (positional)"}),e.jsx(me,{params:s.params,onChange:i=>x(a,{...s,params:i}),urlMode:!1}),e.jsxs("div",{className:l.row,children:[e.jsx(u,{appearance:"subtle",size:"small",onClick:()=>{te(a,!1)},children:"Function preview"}),e.jsx(u,{appearance:"subtle",size:"small",onClick:()=>{te(a,!0)},children:"Open in Monaco"})]}),o&&e.jsxs("div",{className:l.codePanel,children:[e.jsxs("div",{className:l.row,children:[e.jsx(d,{weight:"semibold",children:E?"Monaco Editor":"Function Preview"}),R&&e.jsx(d,{size:200,style:{fontFamily:n.fontFamilyMonospace},children:R})]}),O&&e.jsx(C,{intent:"error",children:e.jsx(w,{children:O})}),re&&e.jsx(C,{intent:"warning",children:e.jsx(w,{children:re})}),ne?e.jsx(d,{style:{color:n.colorNeutralForeground3},children:"Loading source…"}):e.jsx("div",{style:{height:240},children:e.jsx(qe,{language:"javascript",value:le,readOnly:!E,onChange:i=>{F(i),B(!0)},options:{wordWrap:"on"}})}),e.jsxs("div",{className:l.row,children:[e.jsx(u,{appearance:"subtle",size:"small",onClick:()=>{A(null),g(null),B(!1)},children:"Close"}),E&&e.jsx(Oe,{prefix:"Saves to"}),E&&e.jsx(u,{appearance:"primary",size:"small",onClick:()=>{Ee()},disabled:!we||ne||!!O,children:"Save Web Resource"})]})]})]},v.keys[a]);if(s.kind==="url"){const i=s.address.trim().startsWith("https://");return e.jsxs("div",{className:l.actionCard,children:[e.jsxs("div",{className:l.actionHeader,children:[e.jsx(I,{appearance:"filled",color:"informative",size:"small",children:"URL"}),e.jsx(u,{appearance:"subtle",size:"small",icon:e.jsx(N,{}),onClick:()=>W(a),"aria-label":"Remove action"})]}),e.jsx(C,{intent:"warning",children:e.jsx(w,{children:e.jsx(d,{className:l.securityNote,children:"⚠ Ampersands in URLs are automatically escaped as & in the generated XML."})})}),e.jsx(j,{label:"Address",validationState:i?"none":"error",validationMessage:i?void 0:"URL must start with https://",children:e.jsx($,{value:s.address,onChange:(c,t)=>x(a,{...s,address:t.value}),placeholder:"https://..."})}),e.jsx(j,{label:"Open in (WinMode)",children:e.jsxs(J,{value:String(s.winMode??0),onChange:(c,t)=>x(a,{...s,winMode:parseInt(t.value)}),children:[e.jsx("option",{value:"0",children:"Navigate (0)"}),e.jsx("option",{value:"1",children:"Dialog (1)"}),e.jsx("option",{value:"2",children:"Popup (2)"})]})}),e.jsx(j,{children:e.jsxs(u,{appearance:s.passParams?"primary":"subtle",size:"small",onClick:()=>x(a,{...s,passParams:s.passParams?void 0:!0}),children:[s.passParams?"✓ PassParams ON":"PassParams OFF"," — appends typename, type, id, orgname…"]})}),e.jsx(X,{children:"Parameters (named)"}),e.jsx(me,{params:s.params,onChange:c=>x(a,{...s,params:c}),urlMode:!0})]},v.keys[a])}return s.kind==="powerFx"||s.kind==="customApi"?e.jsxs("div",{className:l.actionCard,children:[e.jsxs("div",{className:l.actionHeader,children:[e.jsxs(I,{appearance:"filled",color:"danger",size:"small",children:[s.kind==="powerFx"?"Power Fx":"Custom API"," — not supported"]}),e.jsx(u,{appearance:"subtle",size:"small",icon:e.jsx(N,{}),onClick:()=>W(a),"aria-label":"Remove action"})]}),e.jsx(C,{intent:"error",children:e.jsxs(w,{children:["Classic ribbon commands can't run"," ",s.kind==="powerFx"?"Power Fx":"Custom API"," actions, so this command can't be published while it has one. Remove it and use a JavaScript function instead (for example, call the API with ",e.jsx("code",{children:"Xrm.WebApi.online.execute"}),"), or build the command with modern commanding in the app designer."]})})]},v.keys[a]):s.kind==="outlookCommand"||s.kind==="raw"?e.jsxs("div",{className:l.actionCard,children:[e.jsxs("div",{className:l.actionHeader,children:[e.jsx(I,{appearance:"outline",size:"small",children:s.kind==="outlookCommand"?"Outlook command":s.tagName}),e.jsx(u,{appearance:"subtle",size:"small",icon:e.jsx(N,{}),onClick:()=>W(a),"aria-label":"Remove action"})]}),e.jsx(d,{size:200,style:{color:n.colorNeutralForeground3},children:s.kind==="outlookCommand"?`ActionType="${s.actionType}"${s.data?` Data="${s.data}"`:""}`:s.xml})]},v.keys[a]):null},Ie=()=>e.jsxs(e.Fragment,{children:[r.actions.map((s,a)=>Fe(s,a)),r.actions.length===0&&e.jsx(d,{italic:!0,style:{color:n.colorNeutralForeground3},children:"No actions defined — button will do nothing when clicked."}),e.jsxs("div",{className:l.row,children:[e.jsx(j,{label:"Action type",style:{minWidth:"220px"},children:e.jsxs(J,{onChange:(s,a)=>{const o=a.value;o&&U(o)},value:"",children:[e.jsx("option",{value:"",children:"Add action..."}),e.jsx("option",{value:"javascript",children:"JavaScript function"}),e.jsx("option",{value:"url",children:"URL action"})]})}),e.jsx(u,{icon:e.jsx(ue,{}),appearance:"secondary",size:"small",onClick:()=>U("javascript"),children:"Add JavaScript function"}),e.jsx(u,{icon:e.jsx(ue,{}),appearance:"secondary",size:"small",onClick:()=>U("url"),children:"Add URL action"})]})]}),Me=()=>e.jsxs(e.Fragment,{children:[e.jsx(j,{label:"Command ID",children:e.jsx($,{value:p,onChange:(s,a)=>Q(a.value),style:{fontFamily:n.fontFamilyMonospace}})}),P&&e.jsx(d,{style:{color:n.colorPaletteRedForeground1,fontSize:n.fontSizeBase100},children:P}),e.jsx(u,{appearance:"secondary",size:"small",disabled:!p.trim()||!!P||p===r.id,onClick:()=>{if(!p.trim()||P)return;const s=p.trim();xe(r.id,s)&&pe(s)},children:"Apply ID change"}),e.jsx(d,{style:{color:n.colorNeutralForeground3,fontSize:n.fontSizeBase200},children:"Naming convention: Publisher.EntityName.ButtonName.Command"}),e.jsx(C,{intent:"warning",children:e.jsx(w,{children:e.jsxs(d,{className:l.securityNote,children:["⚠ ",e.jsx("strong",{children:"Security:"})," All command definitions are visible in browser source regardless of button visibility. Do not embed sensitive logic, credentials, or privileged information in command parameters or function names."]})})})]}),ce=s=>{const a=s==="enable"?r.enableRules:r.displayRules,o=(s==="enable"?Y:G).map(t=>t.id).filter(t=>!a.includes(t)),i=s==="enable"?ye:Ce,c=()=>{const t=new Set([...Y.map(_=>_.id),...G.map(_=>_.id)]),h=Ve("New",s==="enable"?"Enable":"Display",t);s==="enable"?fe({id:h,steps:[]}):ge({id:h,steps:[]}),q(h,s)};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:l.rulePills,children:[a.map(t=>e.jsxs("div",{className:l.ruleRow,children:[e.jsx(I,{appearance:"outline",size:"medium",className:l.ruleChip,onClick:()=>q(t,s),role:"button",tabIndex:0,children:t}),e.jsx(u,{appearance:"subtle",size:"small",className:l.ruleRemoveBtn,icon:e.jsx(N,{}),"aria-label":`Unbind ${t}`,onClick:()=>{y(s==="enable"?{enableRules:r.enableRules.filter(h=>h!==t)}:{displayRules:r.displayRules.filter(h=>h!==t)})}})]},t)),a.length===0&&e.jsxs(d,{italic:!0,style:{color:n.colorNeutralForeground3},children:["No ",s," rules — always ",s==="enable"?"enabled":"visible","."]})]}),e.jsxs("div",{className:l.row,children:[e.jsx(j,{label:`Bind existing ${s} rule`,style:{minWidth:"260px"},children:e.jsxs(J,{value:i,onChange:(t,h)=>s==="enable"?Z(h.value):ee(h.value),children:[e.jsx("option",{value:"",children:"Select a rule…"}),o.map(t=>e.jsx("option",{value:t,children:t},t))]})}),e.jsx(u,{appearance:"secondary",size:"small",disabled:!i,onClick:()=>{i&&(s==="enable"?(y({enableRules:[...r.enableRules,i]}),Z("")):(y({displayRules:[...r.displayRules,i]}),ee("")))},children:"Bind selected"}),e.jsxs(u,{appearance:"subtle",size:"small",onClick:c,children:["Create new ",s," rule"]})]}),s==="enable"&&e.jsx(C,{intent:"info",children:e.jsxs(w,{children:["Disabled buttons are ",e.jsx("strong",{children:"hidden"})," in the modern command bar (Unified Interface). In the classic ribbon, disabled buttons remain visible but do not respond to clicks."]})})]})},Ae=()=>e.jsxs(e.Fragment,{children:[e.jsxs(d,{children:["Referenced by ",e.jsx("strong",{children:oe})," button(s)."]}),H.length>0&&e.jsx("div",{className:l.versionsList,children:H.map(s=>e.jsx("div",{className:l.versionRow,children:e.jsxs("div",{children:[e.jsx(d,{weight:"semibold",style:{fontFamily:n.fontFamilyMonospace},children:s.button.id}),e.jsxs(d,{className:l.versionMeta,children:[s.location," → ",s.tab.label," → ",s.group.label]})]})},`${s.location}:${s.tab.id}:${s.group.id}:${s.button.id}`))})]}),de=je.filter(s=>s.target===r.id||s.detail.includes(r.id)).slice().reverse(),De=()=>e.jsx(e.Fragment,{children:de.length===0?e.jsx(d,{style:{color:n.colorNeutralForeground3},children:"No command-specific history entries yet."}):e.jsx("div",{className:l.versionsList,children:de.map((s,a)=>e.jsxs("div",{className:l.versionRow,children:[e.jsxs("div",{children:[e.jsx(d,{weight:"semibold",children:s.op}),e.jsx(d,{className:l.versionMeta,children:s.detail})]}),e.jsx(d,{className:l.versionMeta,children:new Date(s.ts).toLocaleString()})]},`${s.ts}-${a}`))})});return e.jsxs(Ke,{position:"end",open:L,onOpenChange:()=>M(),className:l.drawer,children:[e.jsx(Ge,{children:e.jsx(Ye,{action:e.jsx(u,{appearance:"subtle",icon:e.jsx(N,{}),onClick:M,"aria-label":"Close command editor"}),children:e.jsx(d,{size:400,weight:"semibold",style:{fontFamily:n.fontFamilyMonospace},children:r.id})})}),e.jsxs(Qe,{className:l.body,children:[e.jsxs(Ze,{selectedValue:f,onTabSelect:(s,a)=>ve(a.value),children:[e.jsx(z,{value:"actions",children:"Actions"}),e.jsx(z,{value:"enableRules",children:"Enable rules"}),e.jsx(z,{value:"displayRules",children:"Display rules"}),e.jsx(z,{value:"settings",children:"Settings"}),e.jsxs(z,{value:"references",children:["References (",oe,")"]}),e.jsx(z,{value:"versions",children:"Versions"})]}),e.jsx(X,{}),e.jsx(Le,{commandId:r.id}),e.jsxs("fieldset",{disabled:Re,className:l.readOnly,children:[f==="actions"&&Ie(),f==="enableRules"&&ce("enable"),f==="displayRules"&&ce("display"),f==="settings"&&Me()]}),f==="references"&&Ae(),f==="versions"&&De()]})]})};export{ds as CommandEditorPanel,ds as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{y as a,E as ae,z as ne}from"./fluent-icons-BqJj6rDi.js";import{a as p}from"./monaco-BIO15LCS.js";import{a as s,u as c,an as se,Z as le,ao as ie,ab as x,ai as re,ap as ce,d as de}from"./index-Di2_5czx.js";import{t,B as ue,b as I}from"./fluent-ui-QADO660t.js";import"./dnd-Cq8OftAq.js";const pe=ne({backdrop:{position:"fixed",inset:0,backgroundColor:"rgba(0,0,0,0.5)",zIndex:1e3,display:"flex",alignItems:"flex-start",justifyContent:"center",paddingTop:"10vh"},palette:{width:"640px",maxWidth:"90vw",backgroundColor:t.colorNeutralBackground1,borderRadius:t.borderRadiusLarge,boxShadow:t.shadow64,overflow:"hidden"},searchRow:{display:"flex",alignItems:"center",padding:`${t.spacingVerticalM} ${t.spacingHorizontalM}`,borderBottom:`1px solid ${t.colorNeutralStroke1}`,gap:t.spacingHorizontalS},searchInput:{flex:1,border:"none",outline:"none",fontSize:t.fontSizeBase400,backgroundColor:"transparent"},results:{maxHeight:"400px",overflowY:"auto"},group:{paddingTop:t.spacingVerticalXS},groupLabel:{padding:`${t.spacingVerticalXXS} ${t.spacingHorizontalM}`,color:t.colorNeutralForeground3,fontSize:t.fontSizeBase100,fontWeight:t.fontWeightSemibold,textTransform:"uppercase",letterSpacing:"0.08em"},item:{display:"flex",alignItems:"center",gap:t.spacingHorizontalS,padding:`${t.spacingVerticalXS} ${t.spacingHorizontalM}`,cursor:"pointer","&:hover":{backgroundColor:t.colorNeutralBackground1Hover}},itemSelected:{backgroundColor:t.colorNeutralBackground1Hover},itemLabel:{flex:1},itemSub:{color:t.colorNeutralForeground3,fontSize:t.fontSizeBase200},footer:{padding:`${t.spacingVerticalXS} ${t.spacingHorizontalM}`,borderTop:`1px solid ${t.colorNeutralStroke1}`,color:t.colorNeutralForeground3,fontSize:t.fontSizeBase100,display:"flex",gap:t.spacingHorizontalL}}),ke=()=>{const l=pe(),S=s(e=>e.commandPaletteOpen),j=s(e=>e.setCommandPaletteOpen),D=s(e=>e.openBulkPublish),H=s(e=>e.openXmlDrawer),b=s(e=>e.openImportXml),O=s(e=>e.openCommandEditor),z=s(e=>e.openRuleEditor),P=s(e=>e.openNewGroup),W=s(e=>e.openNewButton),G=s(e=>e.openLocalizationEditor),E=s(e=>e.themeMode),V=s(e=>e.setThemeMode),F=c(e=>e.undo),U=c(e=>e.redo),q=c(e=>e.commands),K=c(e=>e.displayRules),Z=c(e=>e.enableRules),L=c(e=>e.ribbons),w=c(e=>e.locLabels),y=se(),A=le(e=>e.entities),Q=ie(e=>e.requestEntitySwitch),Y=x(e=>e.location),J=x(e=>e.tabId),m=x(e=>e.selectedButtonId),M=x(e=>e.selectedGroupId),[i,R]=p.useState(""),[C,u]=p.useState(0),B=p.useRef(null),n=p.useCallback(()=>{j(!1),R(""),u(0)},[j]);p.useEffect(()=>{S&&setTimeout(()=>B.current?.focus(),50)},[S]);const _=()=>{const e=[],r=L[Y].tabs.find(o=>o.id===J),te=r?.groups[0]?.id??null,N=M??te,X=re(ce(r?.groups.find(o=>o.id===M),m)?.kind),oe=[{id:"publish",label:"Publish all",subtitle:"Save and publish changes to Dataverse",kind:"action",action:()=>{D(),n()}},{id:"view-xml",label:"View RibbonDiffXml",subtitle:"Open XML drawer",kind:"action",action:()=>{H(),n()}},{id:"import-xml",label:"Import XML",subtitle:"Import RibbonDiffXml from clipboard",kind:"action",action:()=>{n();const o="Couldn't read the clipboard here: paste the XML into the editor (Ctrl+V).",g=navigator.clipboard?.readText?.();if(!g)return b("",o);g.then(k=>k.trim().startsWith("<")?b(k):b("","The clipboard doesn't hold XML: paste the XML into the editor."),()=>b("",o))}},{id:"new-group",label:"New group",subtitle:"Create a new ribbon group in current tab",kind:"action",action:()=>{P(),n()}},{id:"new-button",label:"New button",subtitle:N?"Create a new button in selected/current group":"Select a tab/group with at least one group first",kind:"action",action:()=>{N&&(W(N),n())}},{id:"edit-labels",label:"Edit localized labels",subtitle:X?"The selected control is shown read-only":m?"Open localization editor for selected button":"Select a button first",kind:"action",action:()=>{!m||X||(G(m),n())}},{id:"toggle-theme",label:`Toggle theme (${E==="dark"?"Dark":"Light"})`,subtitle:"Switch between light and dark",kind:"action",action:()=>{V(E==="dark"?"light":"dark"),n()}},{id:"undo",label:"Undo",subtitle:"Ctrl+Z",kind:"action",action:()=>{F(),n()}},{id:"redo",label:"Redo",subtitle:"Ctrl+Shift+Z",kind:"action",action:()=>{U(),n()}}];if(e.push(...oe),!i.startsWith("@")&&!i.startsWith("/")){const o=Object.entries(L).flatMap(([g,k])=>k.tabs.flatMap(v=>v.groups.flatMap($=>de($).map(d=>({id:`btn-${d.id}`,label:y(d.label,w,void 0,d.source?.labelToken)||d.id,subtitle:`${g} → ${y(v.label,w,void 0,v.source?.titleToken)} → ${y($.label,w,void 0,$.source?.titleToken)}`,keywords:`${d.label} ${d.id}`,kind:"button",action:()=>{n()}})))));e.push(...o.slice(0,20))}return q.forEach(o=>{e.push({id:`cmd-${o.id}`,label:o.id,subtitle:`${o.actions.length} action(s)`,kind:"command",action:()=>{O(o.id),n()}})}),K.forEach(o=>{e.push({id:`dr-${o.id}`,label:o.id,subtitle:`Display rule — ${o.steps.length} step(s)`,kind:"rule",action:()=>{z(o.id,"display"),n()}})}),Z.forEach(o=>{e.push({id:`er-${o.id}`,label:o.id,subtitle:`Enable rule — ${o.steps.length} step(s)`,kind:"rule",action:()=>{z(o.id,"enable"),n()}})}),(i.startsWith("@")||!i)&&A.slice(0,30).forEach(o=>{e.push({id:`ent-${o.logicalName}`,label:o.displayName,subtitle:o.logicalName,kind:"entity",action:()=>{Q(o),n()}})}),e},h=i.startsWith("@")||i.startsWith("/")?i.slice(1):i,T=_(),f=h?T.filter(e=>e.label.toLowerCase().includes(h.toLowerCase())||(e.subtitle??"").toLowerCase().includes(h.toLowerCase())||(e.keywords??"").toLowerCase().includes(h.toLowerCase())):T,ee=e=>{e.key==="ArrowDown"?(e.preventDefault(),u(r=>Math.min(r+1,f.length-1))):e.key==="ArrowUp"?(e.preventDefault(),u(r=>Math.max(r-1,0))):e.key==="Enter"?f[C]?.action():e.key==="Escape"&&n()};return S?a.jsx("div",{className:l.backdrop,onClick:n,role:"presentation",children:a.jsxs("div",{className:l.palette,onClick:e=>e.stopPropagation(),role:"dialog","aria-modal":"true","aria-label":"Command palette",children:[a.jsxs("div",{className:l.searchRow,children:[a.jsx(ae,{style:{color:t.colorNeutralForeground3,flexShrink:0}}),a.jsx("input",{ref:B,className:l.searchInput,value:i,onChange:e=>{R(e.target.value),u(0)},onKeyDown:ee,placeholder:"Search commands, buttons, entities… Use @ for entities, / for actions","aria-label":"Command palette search"})]}),a.jsxs("div",{className:l.results,role:"listbox",children:[f.map((e,r)=>a.jsxs("div",{className:`${l.item} ${r===C?l.itemSelected:""}`,onClick:e.action,role:"option","aria-selected":r===C,onMouseEnter:()=>u(r),children:[a.jsx(ue,{appearance:"outline",size:"small",children:e.kind}),a.jsxs("div",{className:l.itemLabel,children:[a.jsx(I,{children:e.label}),e.subtitle&&a.jsx(I,{className:l.itemSub,block:!0,children:e.subtitle})]})]},e.id)),f.length===0&&a.jsx("div",{className:l.item,children:a.jsx(I,{style:{color:t.colorNeutralForeground3},children:"No results"})})]}),a.jsxs("div",{className:l.footer,children:[a.jsx("span",{children:"↑↓ navigate"}),a.jsx("span",{children:"↵ select"}),a.jsx("span",{children:"Esc close"}),a.jsx("span",{children:"@ entities"}),a.jsx("span",{children:"/ actions"})]})]})}):null};export{ke as CommandPalette,ke as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{y as e,h as k,
|
|
1
|
+
import{y as e,h as k,bK as C,b4 as D,z as B}from"./fluent-icons-BqJj6rDi.js";import{a as c}from"./monaco-BIO15LCS.js";import{a as u,u as I}from"./index-Di2_5czx.js";import{ai as M,aj as R,ak as H,aa as L,d as N,al as E,b as a,t as o,M as h,f as x,a5 as F,B as z,T as O}from"./fluent-ui-QADO660t.js";import"./dnd-Cq8OftAq.js";function $(){return window.dataverseAPI}function P(i){const l=i.map(s=>{const r=s.publisherid,d=s.versionnumber,m=typeof d=="number"?d:Number(d??0)||0;return{solutionId:String(s.solutionid??""),solutionName:String(s.friendlyname??s.uniquename??"Unknown Solution"),publisher:String(r?.friendlyname??r?.customizationprefix??"Unknown Publisher"),managed:!!s.ismanaged,importedOn:s.modifiedon?String(s.modifiedon):void 0,wins:!1,versionNumber:m}});return l.sort((s,r)=>s.versionNumber-r.versionNumber),l.length>0&&(l[l.length-1].wins=!0),l.map(({versionNumber:s,...r})=>r)}async function A(i){if(!i)return null;const r=(await $().getSolutions(["solutionid","friendlyname","uniquename","ismanaged","publisherid","versionnumber","modifiedon"])).value;return r.length<=1?null:{elementId:i,elementKind:"button",solutions:P(r)}}const T=B({drawer:{width:"560px",maxWidth:"95vw"},body:{display:"flex",flexDirection:"column",gap:o.spacingHorizontalM,padding:o.spacingHorizontalM,overflowY:"auto"},solutionRow:{display:"flex",flexDirection:"column",gap:o.spacingHorizontalXS,padding:o.spacingHorizontalS,border:`1px solid ${o.colorNeutralStroke2}`,borderRadius:o.borderRadiusMedium,backgroundColor:o.colorNeutralBackground2},solutionRowWinner:{border:`2px solid ${o.colorBrandStroke1}`,backgroundColor:o.colorBrandBackground2},solutionHeader:{display:"flex",alignItems:"center",gap:o.spacingHorizontalS},elementId:{fontFamily:o.fontFamilyMonospace,fontSize:o.fontSizeBase200,backgroundColor:o.colorNeutralBackground4,padding:`2px ${o.spacingHorizontalXS}`,borderRadius:o.borderRadiusSmall,wordBreak:"break-all"}}),_=()=>{const i=T(),l=u(n=>n.conflictDrawerOpen),s=u(n=>n.conflictElementId),r=u(n=>n.closeConflictDrawer),d=u(n=>n.openXmlDrawer),m=I(n=>n.conflicts),[p,b]=c.useState(null),[j,f]=c.useState(!1),[v,g]=c.useState(null);c.useEffect(()=>{if(!l||!s){b(null),g(null),f(!1);return}let n=!1;return f(!0),g(null),A(s).then(t=>{n||b(t)}).catch(t=>{n||g(t instanceof Error?t.message:"Failed to load conflict layers")}).finally(()=>{n||f(!1)}),()=>{n=!0}},[l,s]);const w=m.filter(n=>n.elementId===s),y=c.useMemo(()=>p||w[0],[p,w]),S=y?[...y.solutions].sort((n,t)=>n.wins===t.wins?0:n.wins?1:-1):[];return e.jsxs(M,{className:i.drawer,open:l,position:"end",onOpenChange:(n,t)=>!t.open&&r(),children:[e.jsx(R,{children:e.jsx(H,{action:e.jsx(N,{appearance:"subtle",icon:e.jsx(k,{}),onClick:r,"aria-label":"Close"}),children:e.jsx(L,{children:"Solution Conflicts"})})}),e.jsx(E,{children:e.jsxs("div",{className:i.body,children:[s&&e.jsxs("div",{children:[e.jsx(a,{size:200,style:{color:o.colorNeutralForeground3},children:"Conflicting element:"}),e.jsx("div",{className:i.elementId,children:s})]}),e.jsx(h,{intent:"warning",children:e.jsx(x,{children:e.jsx(a,{size:200,children:"Multiple solutions customize this element. Dataverse applies them in solution import order — the most recently imported solution wins."})})}),e.jsx(h,{intent:"info",children:e.jsx(x,{children:e.jsxs(a,{size:200,children:[e.jsx("strong",{children:"To resolve:"})," Merge changes into one solution, or use solution layering to control evaluation order. Managed solutions appear as read-only layers — to resolve, merge the customization into your unmanaged solution."]})})}),j&&e.jsx("div",{style:{display:"flex",justifyContent:"center",padding:o.spacingHorizontalM},children:e.jsx(F,{label:"Loading solution layers..."})}),v&&e.jsx(h,{intent:"error",children:e.jsx(x,{children:v})}),!j&&S.length===0&&e.jsx(a,{style:{color:o.colorNeutralForeground3,textAlign:"center"},children:"No conflict details available from environment layers."}),S.map((n,t)=>e.jsxs("div",{className:`${i.solutionRow} ${n.wins?i.solutionRowWinner:""}`,children:[e.jsxs("div",{className:i.solutionHeader,children:[e.jsx(a,{weight:"semibold",children:n.solutionName}),n.wins&&e.jsx(z,{appearance:"filled",color:"brand",children:"WINS"}),n.managed&&e.jsx(O,{content:"Managed solution — read-only",relationship:"label",children:e.jsx(C,{style:{color:o.colorNeutralForeground3}})}),e.jsx(z,{appearance:"outline",size:"small",children:n.managed?"Managed":"Unmanaged"})]}),e.jsxs(a,{size:200,style:{color:o.colorNeutralForeground3},children:["Publisher: ",n.publisher]}),n.importedOn&&e.jsxs(a,{size:200,style:{color:o.colorNeutralForeground3},children:["Imported: ",new Date(n.importedOn).toLocaleString()]}),e.jsx("div",{style:{display:"flex",gap:o.spacingHorizontalS,flexWrap:"wrap"},children:e.jsx(N,{appearance:"subtle",size:"small",icon:e.jsx(D,{}),onClick:d,children:"View XML diff"})})]},`${n.solutionId}-${t}`))]})})]})};export{_ as ConflictDrawer,_ as default};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./monaco-BIO15LCS.js","./monaco-DdLsZAcA.css"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{R as g,a as b,_ as H}from"./monaco-BIO15LCS.js";import{y as s,h as E,z as C}from"./fluent-icons-
|
|
2
|
+
import{R as g,a as b,_ as H}from"./monaco-BIO15LCS.js";import{y as s,h as E,z as C}from"./fluent-icons-BqJj6rDi.js";import{a as x,u,g as N}from"./index-Di2_5czx.js";import{ai as O,aj as _,ak as V,aa as $,d as S,al as X,M as I,f as T,b as A,B as D,a5 as G,t as r}from"./fluent-ui-QADO660t.js";import"./dnd-Cq8OftAq.js";const P=g.lazy(()=>H(()=>import("./monaco-BIO15LCS.js").then(n=>n.i),__vite__mapDeps([0,1]),import.meta.url).then(n=>({default:n.DiffEditor}))),W=["HomepageGrid","SubGrid","Form","Application"],U=C({drawer:{},body:{display:"flex",flexDirection:"column",flex:"1 1 auto",minHeight:0,overflow:"hidden",gap:r.spacingHorizontalS},statsBar:{display:"flex",alignItems:"center",gap:r.spacingHorizontalS,padding:`${r.spacingVerticalXS} ${r.spacingHorizontalM}`,flexShrink:0},toggleRow:{display:"flex",alignItems:"center",gap:r.spacingHorizontalS,padding:`${r.spacingVerticalXS} ${r.spacingHorizontalM}`,borderBottom:`1px solid ${r.colorNeutralStroke2}`,flexShrink:0},editorWrap:{flex:1,height:0,minHeight:"400px",overflow:"hidden",position:"relative"}});function F(n,o){const l=n.split(`
|
|
3
3
|
`),c=o.split(`
|
|
4
4
|
`);let d=0,a=0;const f=new Set(l),t=new Set(c);for(const i of c)f.has(i)||d++;for(const i of l)t.has(i)||a++;return{added:d,removed:a}}function B(n,o,l,c,d,a,f){const t=[];for(const i of W){const p=f?o[i]:n[i],m=o[i];try{t.push(`<!-- ${i} -->
|
|
5
5
|
`+N({location:i,current:p,baseline:m,commands:l,displayRules:c,enableRules:d,locLabels:a,entityLogicalName:p.entityLogicalName}))}catch(h){t.push(`<!-- ${i}: Error generating XML: ${String(h)} -->`)}}return t.join(`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{y as c}from"./fluent-icons-BqJj6rDi.js";import{a as s,u as n,an as b,aH as p,aI as m,ap as f}from"./index-Di2_5czx.js";import"./monaco-BIO15LCS.js";import"./fluent-ui-QADO660t.js";import"./dnd-Cq8OftAq.js";const R=()=>{const o=s(e=>e.hideInsteadOfDelete),a=s(e=>e.closeHideInsteadOfDelete),i=s(e=>e.announce),t=n(e=>o?f(e.ribbons[o.location].tabs.find(d=>d.id===o.tabId)?.groups.find(d=>d.id===o.groupId),o.buttonId):void 0),l=n(e=>o?e.baseline[o.location]:void 0),r=n(e=>e.locLabels),u=b();return!o||!t||!l?null:c.jsx(p,{open:!0,buttonLabel:u(t.label,r,void 0,t.source?.labelToken),commandId:t.commandId,deleteBlockedReason:m(t,l),onConfirmHide:()=>{n.getState().hideOobButton(o.location,o.tabId,o.groupId,o.buttonId),i("Button hidden"),a()},onConfirmDisplayRule:()=>{n.getState().hideWithDisplayRules(t.commandId)&&i("Button hidden"),a()},onCancel:a})};export{R as HideInsteadOfDeleteModal,R as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{y as o,h as pe,E as Y,J as me,z as he}from"./fluent-icons-BqJj6rDi.js";import{u as be}from"./useReplaceWebResourceConfirm-BUWBVNSC.js";import{a as Q,u as fe,ab as I,Z as J,l as K,au as ye,av as ee,aw as we,E as Se,j as xe,ax as ke,P as L,ay as Ce,az as ve,aA as Re,aB as je,k as W}from"./index-Di2_5czx.js";import{a as l,R as oe}from"./monaco-BIO15LCS.js";import{D as Be,i as Ie,k as Ae,j as Pe,aa as Ne,d as A,l as $e,ac as Te,ad as M,I as H,C as Ee,O as ze,a1 as Fe,t as r,b as p,B as De,m as Le}from"./fluent-ui-QADO660t.js";import"./dnd-Cq8OftAq.js";const We=[{category:"Actions",keywords:["Add","Delete","Edit","Save","Copy","Paste","Undo","Redo","Send","Print","Refresh"]},{category:"Communication",keywords:["Mail","Chat","Phone","Call","Message"]},{category:"Navigation",keywords:["Arrow","Chevron","Navigation","Home","Back","Forward"]},{category:"System",keywords:["Settings","Info","Warning","Dismiss","Question","Checkmark","Error"]},{category:"Security",keywords:["Lock","Shield","Key","Fingerprint"]},{category:"People",keywords:["Person","People","Contact","User"]},{category:"Business",keywords:["Building","Briefcase","Money","Credit","Cart","Tag","Receipt"]},{category:"Files",keywords:["Document","Folder","Attach","Upload","Download","File"]},{category:"Web",keywords:["Globe","Link","Open","Browser","World"]},{category:"Time",keywords:["Calendar","Clock","Timer","History"]},{category:"Rating",keywords:["Star","Heart","Thumb"]},{category:"View",keywords:["Eye","Zoom","View","Window","Preview"]},{category:"Layout",keywords:["Grid","List","Table","Panel","Column","Row"]},{category:"Development",keywords:["Code","Bug","Branch","Wrench","Tool","Terminal"]},{category:"Media",keywords:["Play","Pause","Stop","Video","Audio","Camera"]},{category:"Analytics",keywords:["Chart","Data","Poll","Graph","Trend"]}],Me=["Add","Delete","Edit","Save","Copy","Cut","ClipboardPaste","Share","Mail","Chat","Phone","Search","Filter","ArrowLeft","ArrowRight","ArrowUp","ArrowDown","ChevronDown","ChevronUp","Navigation","Home","Settings","Info","Warning","Checkmark","Dismiss","QuestionCircle","LockClosed","LockOpen","Shield","Person","People","PersonAdd","Building","Briefcase","Money","CreditCard","Cart","Tag","Document","DocumentBulletList","Folder","FolderOpen","Attach","ArrowDownload","ArrowUpload","Globe","Link","Open","Calendar","Clock","Timer","Star","Heart","ThumbLike","ThumbDislike","Eye","EyeOff","ZoomIn","ZoomOut","Maximize","Minimize","Grid","List","Table","TableSimple","Code","Bug","Play","Pause","Stop","Refresh","ArrowSync","ArrowUndo","ArrowRedo","Printer","Send","Rocket","Sparkle","Trophy","Lightbulb","Bot","DataBar","ChartMultiple","Poll"];function He(n){for(const y of We)if(y.keywords.some(m=>n.includes(m)))return y.category;return"General"}const re=Me.map(n=>({name:n,category:He(n)})),Ue=["All",...Array.from(new Set(re.map(n=>n.category))).sort()],Ge=he({surface:{width:"720px",maxWidth:"95vw",height:"min(580px, 90vh)",maxHeight:"90vh",overflow:"hidden"},dialogBody:{display:"flex",flexDirection:"column",height:"100%",minHeight:0},body:{display:"flex",flexDirection:"column",flex:1,minHeight:0,overflow:"hidden",gap:r.spacingHorizontalS},toolbar:{display:"flex",alignItems:"center",gap:r.spacingHorizontalS,flexWrap:"wrap",flexShrink:0},categoryPicker:{minWidth:"180px",maxWidth:"220px"},iconGrid:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(72px, 1fr))",gap:r.spacingHorizontalXS,overflowY:"auto",flex:1,minHeight:0,padding:r.spacingHorizontalXS},iconTile:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:"4px",padding:r.spacingHorizontalXS,borderRadius:r.borderRadiusMedium,border:"1px solid transparent",cursor:"pointer",minHeight:"64px",":hover":{backgroundColor:r.colorNeutralBackground3,border:`1px solid ${r.colorNeutralStroke1}`}},iconTileSelected:{backgroundColor:r.colorBrandBackground2,border:`1px solid ${r.colorBrandStroke1}`},iconLabel:{fontSize:r.fontSizeBase100,textAlign:"center",lineHeight:1.2,wordBreak:"break-word",color:r.colorNeutralForeground2},webResourceList:{flex:1,overflowY:"auto"},webResourceRow:{display:"flex",alignItems:"center",gap:r.spacingHorizontalS,padding:`${r.spacingHorizontalXS} ${r.spacingHorizontalS}`,cursor:"pointer",borderRadius:r.borderRadiusMedium,":hover":{backgroundColor:r.colorNeutralBackground3}},uploadArea:{flex:1,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:r.spacingHorizontalM,border:`2px dashed ${r.colorNeutralStroke2}`,borderRadius:r.borderRadiusMedium,color:r.colorNeutralForeground3}}),Ye=()=>{const n=Ge(),y=be(),m=Q(e=>e.iconPickerOpen),w=Q(e=>e.closeIconPicker),U=fe(e=>e.setButtonProp),v=I(e=>e.selectedButtonId),R=I(e=>e.selectedGroupId),G=I(e=>e.tabId),O=I(e=>e.location),P=J(e=>e.publisherPrefix),S=J(e=>e.activeSolution),[V,c]=l.useState(null),[_,j]=l.useState(null),[u,q]=l.useState("fluent"),[N,ae]=l.useState(""),[B,se]=l.useState("All"),[x,ne]=l.useState("regular"),[i,te]=l.useState(null),[k,$]=l.useState(""),[T,E]=l.useState([]),[X,Z]=l.useState(!1),[z,le]=l.useState(""),[b,f]=l.useState(!1);oe.useEffect(()=>{m||(c(null),j(null))},[m]),oe.useEffect(()=>{!m||u!=="webresource"||(Z(!0),K("svg").then(e=>E(e)).catch(()=>E([])).finally(()=>Z(!1)))},[m,u]);const ie=l.useMemo(()=>{const e=z.toLowerCase();return T.filter(a=>!e||a.name.toLowerCase().includes(e))},[z,T]),ce=l.useMemo(()=>{const e=N.toLowerCase();return re.filter(a=>(B==="All"||a.category===B)&&(e===""||a.name.toLowerCase().includes(e)))},[N,B]),de=async()=>{if(!i||!v||!R)return;const e=W(),a=L.getState();if(c(null),!S||S.isManaged){c("Select an unmanaged solution first — the icon is added to it as an SVG web resource.");return}f(!0);try{const s=await Ce(i,x,P,ve,{busy:{onProgress:t=>a.logInfo("webresource",t)},guard:e});s.publishError&&a.logWarn("webresource",`Publishing icon web resource ${s.name} failed: ${s.publishError}. Pick the icon again or publish customizations before using the button.`),U(O,G,R,v,{icon:ee(i),image16:`$webresource:${s.name}`,image32:`$webresource:${s.name}`}),a.logInfo("webresource",`${s.created?"Uploaded":"Reusing"} icon web resource ${s.name} (solution ${S.uniqueName}).`),w()}catch(s){const t=s instanceof Error?s.message:String(s);a.logError("webresource",`Fluent icon "${i}" could not be added as a web resource: ${t}`),c(`Could not add the icon as a web resource: ${t}`)}finally{f(!1)}},ue=async()=>{if(!k||!v||!R)return;const e=Re(k),a=W(),s=L.getState();c(null),j(null);const t=e.startsWith("$webresource:")?e.slice(13).trim():null;let g=null;if(t){f(!0);try{const h=T.find(F=>F.name===t),d=await je(t,h,a);d.solutionUniqueName&&s.logInfo("webresource",`Added web resource ${t} to solution ${d.solutionUniqueName}.`),g=d.solutionWarning??null}catch(h){const d=h instanceof Error?h.message:String(h);if(/^No web resource named /.test(d)){c(d);return}g=`The icon was set, but ${t} could not be added to the solution: ${d}`,s.logError("webresource",g)}finally{f(!1)}}if(U(O,G,R,v,{image16:e,image32:e}),g){j(g);return}w()},ge=async e=>{const a=W();if(!S||S.isManaged){c("Select an unmanaged solution first — the SVG is added to it as a web resource.");return}c(null),f(!0);try{const s=await new Promise((d,F)=>{const C=new FileReader;C.onerror=()=>F(C.error??new Error("Failed to read SVG")),C.onload=()=>{const D=String(C.result??"");d(D.includes(",")?D.split(",")[1]??"":D)},C.readAsDataURL(e)}),t=`${P||"new"}_/icons/${e.name}`,g=await xe(t,s,11,{displayName:e.name,guard:a,onExisting:y.onExisting});j([g.solutionWarning,g.publishWarning].filter(Boolean).join(" ")||null),$(`$webresource:${t}`),q("webresource");const h=await K("svg");E(h)}catch(s){if(s instanceof ke)return;const t=s instanceof Error?s.message:String(s);L.getState().logError("webresource",`SVG upload failed: ${t}`),c(`Upload failed: ${t}`)}finally{f(!1)}};return o.jsxs(o.Fragment,{children:[y.dialog,o.jsx(Be,{open:m,onOpenChange:(e,a)=>!a.open&&w(),children:o.jsx(Ie,{className:n.surface,children:o.jsxs(Ae,{className:n.dialogBody,children:[o.jsx(Pe,{action:o.jsx(A,{appearance:"subtle",icon:o.jsx(pe,{}),onClick:w,"aria-label":"Close"}),children:o.jsx(Ne,{children:"Choose Icon"})}),o.jsxs($e,{className:n.body,children:[o.jsxs(Te,{selectedValue:u,onTabSelect:(e,a)=>q(a.value),size:"small",children:[o.jsx(M,{value:"fluent",children:"Fluent icons"}),o.jsx(M,{value:"webresource",children:"Web resources"}),o.jsx(M,{value:"upload",children:"Upload SVG"})]}),u==="fluent"&&o.jsxs(o.Fragment,{children:[o.jsxs("div",{className:n.toolbar,children:[o.jsx(H,{contentBefore:o.jsx(Y,{}),placeholder:"Search icons…",value:N,onChange:(e,a)=>ae(a.value),size:"small",style:{flex:1}}),o.jsx(Ee,{className:n.categoryPicker,placeholder:"Category",value:B,onOptionSelect:(e,a)=>se(String(a.optionValue??"All")),children:Ue.map(e=>o.jsx(ze,{value:e,text:e,children:e},e))}),o.jsx(Fe,{label:x==="regular"?"Regular":"Filled",checked:x==="filled",onChange:(e,a)=>ne(a.checked?"filled":"regular")})]}),o.jsx("div",{className:n.iconGrid,children:ce.map(e=>o.jsxs("div",{className:`${n.iconTile} ${i===e.name?n.iconTileSelected:""}`,onClick:()=>te(e.name),role:"option","aria-selected":i===e.name,title:`${e.name} — ${e.category}`,children:[o.jsx(ye,{name:ee(e.name),size:24,variant:x}),o.jsx("span",{className:n.iconLabel,children:e.name})]},e.name))}),i&&o.jsxs("div",{style:{display:"flex",alignItems:"center",gap:r.spacingHorizontalS,padding:r.spacingHorizontalXS,backgroundColor:r.colorNeutralBackground3,borderRadius:r.borderRadiusMedium,flexShrink:0},children:[o.jsxs(p,{size:200,style:{fontFamily:r.fontFamilyMonospace},children:['ModernImage="$webresource:',we(P||"new",i,x),'"']}),o.jsx(De,{appearance:"tint",color:"success",children:"Selected"})]})]}),u==="webresource"&&o.jsxs("div",{style:{display:"flex",flexDirection:"column",gap:r.spacingHorizontalS,flex:1},children:[o.jsx(H,{contentBefore:o.jsx(Y,{}),placeholder:"Search SVG web resources...",value:z,onChange:(e,a)=>le(a.value),size:"small"}),o.jsxs("div",{className:n.webResourceList,children:[X&&o.jsx(p,{size:200,style:{color:r.colorNeutralForeground3},children:"Loading web resources..."}),!X&&ie.slice(0,200).map(e=>{const a=`$webresource:${e.name}`,s=k===a;return o.jsx("div",{className:n.webResourceRow,onClick:()=>$(a),style:{backgroundColor:s?r.colorBrandBackground2:void 0},children:o.jsx(p,{size:200,style:{fontFamily:r.fontFamilyMonospace},children:a})},e.id)})]}),o.jsx(H,{placeholder:"$webresource:publisher_/icons/…",value:k,onChange:(e,a)=>$(a.value)}),o.jsx(p,{size:100,style:{color:r.colorNeutralForeground3},children:"Both image16 and image32 will be set to this path. Use SVG for best results across all DPI levels."})]}),u==="upload"&&o.jsxs("div",{className:n.uploadArea,children:[o.jsx(me,{fontSize:48,style:{opacity:.4}}),o.jsx(p,{children:"Drag an SVG file here, or click to browse"}),o.jsx(p,{size:200,style:{color:r.colorNeutralForeground3},children:"The file will be uploaded as a web resource in the active solution."}),o.jsx("input",{type:"file",accept:".svg",style:{display:"none"},id:"icon-upload-input",disabled:b,onChange:e=>{const a=e.target.files?.[0];e.target.value="",a&&ge(a)}}),o.jsx("label",{htmlFor:"icon-upload-input",style:{display:"inline-block",cursor:"pointer",padding:"6px 12px",background:r.colorBrandBackground,color:r.colorNeutralForegroundOnBrand,borderRadius:r.borderRadiusMedium,fontSize:r.fontSizeBase300},children:b?"Uploading...":"Browse for SVG"})]}),V&&o.jsx(p,{role:"alert",size:200,style:{color:r.colorPaletteRedForeground1,flexShrink:0},children:V}),_&&o.jsxs(p,{role:"status",size:200,style:{color:r.colorPaletteMarigoldForeground1,flexShrink:0},children:["Icon applied. ",_]})]}),o.jsxs(Le,{children:[o.jsx(Se,{prefix:"Saves to"}),o.jsx("div",{style:{flex:1}}),o.jsx(A,{appearance:"secondary",onClick:w,children:"Cancel"}),u==="fluent"&&o.jsx(A,{appearance:"primary",disabled:!i||b,onClick:()=>{de()},children:b?"Adding icon…":`Use ${i??"icon"}`}),u==="webresource"&&o.jsx(A,{appearance:"primary",disabled:!k||b,onClick:()=>{ue()},children:b?"Adding icon…":"Use web resource"})]})]})})})]})};export{Ye as IconPickerModal,Ye as default};
|