flexdesk 0.17.16 → 0.17.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +0 -0
  3. package/README.md +0 -0
  4. package/THIRD_PARTY_LICENSES.md +0 -0
  5. package/canvas.js +0 -0
  6. package/charts.js +0 -0
  7. package/core.js +0 -0
  8. package/css/base.css +32 -1
  9. package/css/flexdesk.css +32 -1
  10. package/css/reset.css +0 -0
  11. package/css/tokens.css +3 -0
  12. package/dist/canvas.js +0 -0
  13. package/dist/canvas.js.map +0 -0
  14. package/dist/chunk-3PHCPZHT.js +0 -0
  15. package/dist/chunk-3PHCPZHT.js.map +0 -0
  16. package/dist/chunk-FOOS3T5L.js +0 -0
  17. package/dist/chunk-FOOS3T5L.js.map +0 -0
  18. package/dist/chunk-GOQLR5WR.js +0 -0
  19. package/dist/chunk-GOQLR5WR.js.map +0 -0
  20. package/dist/{chunk-JKZLNKG6.js → chunk-HKPB6OFA.js} +90 -25
  21. package/dist/{chunk-JKZLNKG6.js.map → chunk-HKPB6OFA.js.map} +2 -2
  22. package/dist/chunk-HNU6MM2B.js +0 -0
  23. package/dist/chunk-HNU6MM2B.js.map +0 -0
  24. package/dist/chunk-JYWURG5T.js +0 -0
  25. package/dist/chunk-JYWURG5T.js.map +0 -0
  26. package/dist/chunk-N7HC3ADV.js +0 -0
  27. package/dist/chunk-N7HC3ADV.js.map +0 -0
  28. package/dist/editor.js.map +0 -0
  29. package/dist/flexdesk.css +32 -1
  30. package/dist/flow.js.map +0 -0
  31. package/dist/host.js +0 -0
  32. package/dist/host.js.map +0 -0
  33. package/dist/reset.css +0 -0
  34. package/dist/tile_registry-6WZPTUZV.js +0 -0
  35. package/dist/tile_registry-6WZPTUZV.js.map +0 -0
  36. package/dist/tokens.css +3 -0
  37. package/dist/widgets.js +4115 -4089
  38. package/dist/widgets.js.map +4 -4
  39. package/dist/wm.js +115 -31
  40. package/dist/wm.js.map +2 -2
  41. package/docs/components/command-palette.md +0 -0
  42. package/docs/components/conversation.md +0 -0
  43. package/docs/components/data-table-grid.md +0 -0
  44. package/docs/components/data-table.md +0 -0
  45. package/docs/components/editor.md +0 -0
  46. package/docs/components/flow-canvas-editor.md +0 -0
  47. package/docs/components/flow-editors.md +0 -0
  48. package/docs/components/flow-lane-editor.md +0 -0
  49. package/docs/components/flow-outline-editor.md +0 -0
  50. package/docs/components/input-fields.md +0 -0
  51. package/docs/components/markdown-and-outline.md +0 -0
  52. package/docs/components/menus.md +38 -1
  53. package/docs/components/notifications.md +23 -1
  54. package/docs/components/panels-and-desktops.md +22 -0
  55. package/docs/components/record-list-and-fact-table.md +0 -0
  56. package/docs/components/settings-view.md +0 -0
  57. package/docs/components/shell.md +54 -0
  58. package/docs/components/wizard.md +0 -0
  59. package/docs/concepts/building-and-packaging.md +0 -0
  60. package/docs/concepts/domain-neutrality.md +0 -0
  61. package/docs/concepts/host-port.md +0 -0
  62. package/docs/concepts/packages-and-entry-points.md +0 -0
  63. package/docs/concepts/peer-dependencies.md +0 -0
  64. package/docs/quickstart.md +0 -0
  65. package/editor.js +0 -0
  66. package/flow.js +0 -0
  67. package/host.js +0 -0
  68. package/package.json +1 -1
  69. package/src/canvas/canvas_adapter.js +0 -0
  70. package/src/canvas/edge_router.js +0 -0
  71. package/src/canvas/graph/connector_router.js +0 -0
  72. package/src/canvas/graph/history_service.js +0 -0
  73. package/src/canvas/graph/node_platform.js +0 -0
  74. package/src/canvas/index.js +0 -0
  75. package/src/canvas/layered_layout.js +0 -0
  76. package/src/charts/data_series_plot_window.js +0 -0
  77. package/src/charts/downsample.js +0 -0
  78. package/src/charts/plot_config.js +0 -0
  79. package/src/charts/plotly_wrapper.js +0 -0
  80. package/src/core/event_bus.js +0 -0
  81. package/src/core/logging.js +0 -0
  82. package/src/core/settings.js +0 -0
  83. package/src/core/state_guard.js +0 -0
  84. package/src/core/state_machine.js +0 -0
  85. package/src/editor/editor_pane.js +0 -0
  86. package/src/editor/monaco_editor_factory.js +0 -0
  87. package/src/editor/monaco_loader.js +0 -0
  88. package/src/editor/notebook_search.js +0 -0
  89. package/src/editor/notebook_undo_manager.js +0 -0
  90. package/src/editor/split_pane_container.js +0 -0
  91. package/src/flow/canvas_editor/connections.js +0 -0
  92. package/src/flow/canvas_editor/editor.js +0 -0
  93. package/src/flow/canvas_editor/index.js +0 -0
  94. package/src/flow/canvas_editor/layout.js +0 -0
  95. package/src/flow/canvas_editor/strings.js +0 -0
  96. package/src/flow/kit/always_before.js +0 -0
  97. package/src/flow/kit/catalogue.js +0 -0
  98. package/src/flow/kit/chip_input.js +0 -0
  99. package/src/flow/kit/dom.js +0 -0
  100. package/src/flow/kit/findings.js +0 -0
  101. package/src/flow/kit/history.js +0 -0
  102. package/src/flow/kit/index.js +0 -0
  103. package/src/flow/kit/keys.js +0 -0
  104. package/src/flow/kit/logic_graph.js +0 -0
  105. package/src/flow/kit/references.js +0 -0
  106. package/src/flow/kit/settings_panel.js +0 -0
  107. package/src/flow/kit/settings_schema.js +0 -0
  108. package/src/flow/kit/step_picker.js +0 -0
  109. package/src/flow/kit/strings.js +0 -0
  110. package/src/flow/kit/value_picker.js +0 -0
  111. package/src/flow/kit/widgets.js +0 -0
  112. package/src/flow/lanes/editor.js +0 -0
  113. package/src/flow/lanes/index.js +0 -0
  114. package/src/flow/lanes/layout.js +0 -0
  115. package/src/flow/lanes/pipeline.js +0 -0
  116. package/src/flow/lanes/preview.js +0 -0
  117. package/src/flow/lanes/strings.js +0 -0
  118. package/src/flow/outline/editor.js +0 -0
  119. package/src/flow/outline/index.js +0 -0
  120. package/src/flow/outline/mapping.js +0 -0
  121. package/src/flow/outline/menu.js +0 -0
  122. package/src/flow/outline/operations.js +0 -0
  123. package/src/flow/outline/recognise.js +0 -0
  124. package/src/flow/outline/strings.js +0 -0
  125. package/src/help/help_modal.js +0 -0
  126. package/src/help/help_registry.js +0 -0
  127. package/src/host/external_links.js +0 -0
  128. package/src/host/file_picker.js +0 -0
  129. package/src/host/host.js +0 -0
  130. package/src/host/photino_host.js +0 -0
  131. package/src/host/photino_host.md +0 -0
  132. package/src/host/pywebview_host.js +0 -0
  133. package/src/tiles/chart_tiles.js +0 -0
  134. package/src/tiles/config_schema.js +0 -0
  135. package/src/tiles/figure_tile.js +0 -0
  136. package/src/tiles/layout_persistence.js +0 -0
  137. package/src/tiles/list_tiles.js +0 -0
  138. package/src/tiles/scope_picker.js +0 -0
  139. package/src/tiles/tile_config_form.js +0 -0
  140. package/src/tiles/tile_registry.js +0 -0
  141. package/src/tiling/bottom_panel_tabs.js +0 -0
  142. package/src/tiling/command_palette.js +0 -0
  143. package/src/tiling/content_registry.js +0 -0
  144. package/src/tiling/entity_sources.js +0 -0
  145. package/src/tiling/kind_taxonomy.js +21 -13
  146. package/src/tiling/landing_table.js +0 -0
  147. package/src/tiling/loading_overlay.js +0 -0
  148. package/src/tiling/nav_panel.js +0 -0
  149. package/src/tiling/page_factory.js +0 -0
  150. package/src/tiling/panel_availability.js +0 -0
  151. package/src/tiling/panel_keys.js +0 -0
  152. package/src/tiling/shell.js +4 -1
  153. package/src/tiling/tab_strip.js +0 -0
  154. package/src/tiling/tile_renderer.js +0 -0
  155. package/src/tiling/tile_tab_menu.js +0 -0
  156. package/src/tiling/wm.js +123 -28
  157. package/src/tiling/zoom.js +0 -0
  158. package/src/ui/base/component_base.js +0 -0
  159. package/src/ui/base/controller_base.js +0 -0
  160. package/src/ui/base/page_base.js +0 -0
  161. package/src/ui/components/about_dialog.js +0 -0
  162. package/src/ui/components/alert_log.js +0 -0
  163. package/src/ui/components/alerts_pane.js +0 -0
  164. package/src/ui/components/attribute_list_editor.js +0 -0
  165. package/src/ui/components/autocomplete_field.js +0 -0
  166. package/src/ui/components/cell_editor_host.js +0 -0
  167. package/src/ui/components/cell_editors.js +0 -0
  168. package/src/ui/components/cell_grid.js +0 -0
  169. package/src/ui/components/cell_paste.js +0 -0
  170. package/src/ui/components/cell_selection.js +0 -0
  171. package/src/ui/components/choice_controls.js +0 -0
  172. package/src/ui/components/color_field.js +0 -0
  173. package/src/ui/components/color_list_field.js +0 -0
  174. package/src/ui/components/column_fit.js +0 -0
  175. package/src/ui/components/column_groups.js +0 -0
  176. package/src/ui/components/column_order.js +0 -0
  177. package/src/ui/components/conversation_view.js +0 -0
  178. package/src/ui/components/crosstab.js +0 -0
  179. package/src/ui/components/detail_header.js +0 -0
  180. package/src/ui/components/document_outline.js +0 -0
  181. package/src/ui/components/drag_reorder.js +0 -0
  182. package/src/ui/components/fact_table.js +0 -0
  183. package/src/ui/components/fill_handle.js +0 -0
  184. package/src/ui/components/gallery_picker.js +0 -0
  185. package/src/ui/components/header_actions.js +0 -0
  186. package/src/ui/components/inline_renamer.js +0 -0
  187. package/src/ui/components/job_progress.js +0 -0
  188. package/src/ui/components/menu_bar.js +101 -24
  189. package/src/ui/components/notification_history.js +11 -1
  190. package/src/ui/components/progress.js +0 -0
  191. package/src/ui/components/record_list.js +0 -0
  192. package/src/ui/components/row_activation.js +0 -0
  193. package/src/ui/components/section_stack.js +0 -0
  194. package/src/ui/components/series.js +0 -0
  195. package/src/ui/components/settings_view.js +0 -0
  196. package/src/ui/components/slide_out_panel.js +0 -0
  197. package/src/ui/components/slider_field.js +0 -0
  198. package/src/ui/components/sortable_list.js +0 -0
  199. package/src/ui/components/staged_edits.js +0 -0
  200. package/src/ui/components/staged_history.js +0 -0
  201. package/src/ui/components/table_filter_chips.js +0 -0
  202. package/src/ui/components/table_state_store.js +0 -0
  203. package/src/ui/components/table_structure.js +0 -0
  204. package/src/ui/components/table_values.js +0 -0
  205. package/src/ui/components/tabs.js +0 -0
  206. package/src/ui/components/tag_input.js +0 -0
  207. package/src/ui/components/toast.js +0 -0
  208. package/src/ui/components/toggle_field.js +0 -0
  209. package/src/ui/components/tsv.js +0 -0
  210. package/src/ui/components/window_taskbar.js +0 -0
  211. package/src/ui/controllers/window_chrome_controller.js +4 -1
  212. package/src/ui/notification_center.js +47 -1
  213. package/src/ui/utils/auto_scrollbars.js +0 -0
  214. package/src/ui/utils/markdown.js +0 -0
  215. package/src/ui/utils/native_context_menu.js +0 -0
  216. package/src/ui/utils/overlay_scrollbar.js +0 -0
  217. package/src/ui/utils/raf_resize_observer.js +0 -0
  218. package/src/ui/utils/scroll_lock.js +0 -0
  219. package/src/ui/utils/strip_scroll.js +0 -0
  220. package/src/ui/utils/tooltip_service.js +0 -0
  221. package/tiles.js +0 -0
  222. package/wm.js +0 -0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -66,7 +66,8 @@ bar.setChecked('dark', true); // …and unchecks the rest of the radio group
66
66
  `createMenuBar(el, opts)` returns a `MenuBar` (also exported, for
67
67
  `instanceof`). Its methods: `open(menuId, { focusItem })`, `close()`,
68
68
  `focus()`, `isOpen()`, `isEnabled(id)`, `isChecked(id)`, `setEnabled(id, on)`,
69
- `setChecked(id, on)`, `commands()` and `dispose()`.
69
+ `setChecked(id, on)`, `commands()`, `setStyle(style)` (0.17.18, below) and
70
+ `dispose()`.
70
71
 
71
72
  - **Pointer.** Clicking a title opens its menu; while one is open, hovering
72
73
  another title switches to it. Hovering an item with a submenu opens it.
@@ -101,6 +102,42 @@ The bar is `.twm-menubar` (not the older `.twm-menu-bar` icon strip).
101
102
  `tests/menu_bar.test.mjs` drives it in jsdom; `tests/menu_bar_css.test.mjs`
102
103
  holds its stylesheet.
103
104
 
105
+ ## A hamburger instead of the bar: `style: 'hamburger'` (0.17.18)
106
+
107
+ The same menus can be drawn as one icon button in place of the row of titles,
108
+ for a top bar with no room for File / Edit / Help. Its menu lists the menus,
109
+ and each one flies out its items: ☰ › File › Open…
110
+
111
+ ```js
112
+ const bar = createMenuBar(el, { menus, onAction, style: 'hamburger', label: 'Main menu' });
113
+ bar.setStyle('bar'); // and back: bar.setStyle('hamburger')
114
+ bar.style; // 'bar' | 'hamburger'
115
+ ```
116
+
117
+ - **Options.** `style` is `'bar'` (the default) or `'hamburger'`; anything
118
+ else is a boot error. `icon` is the button's Material Symbols glyph
119
+ (`'menu'`). `label` names the button (its `aria-label` and its tooltip).
120
+ - **The same model.** `onAction`, `setEnabled`, `setChecked`, `isEnabled`,
121
+ `isChecked` and `commands()` work as in the bar; an item fires with its own
122
+ id, never a hamburger id.
123
+ - **`open(menuId, { focusItem })`** opens the hamburger with that menu flown
124
+ out, focus on its row (or, with `focusItem`, on its first item).
125
+ - **Keyboard.** Alt or F10 focuses the button; Down, Enter or Space opens it.
126
+ In its menu Up/Down move across the menus, Right flies one out and Left
127
+ closes it. There is no next or previous title, so Left/Right on the button
128
+ itself, and at the top level of its menu, go nowhere. Esc closes one level.
129
+ - **`setStyle(style)`** switches at run time (a setting, a narrow window). It
130
+ closes whatever is open and keeps every `setEnabled` / `setChecked`; if the
131
+ bar had focus, the first title (or the button) gets it.
132
+ - **Styling.** The bar gets `.twm-menubar--hamburger`, the button
133
+ `.twm-menubar__title--icon`, the size of the top bar's other icon buttons
134
+ (`.twm-panel-toggle-btn`).
135
+
136
+ In a shell, pass it with the model: `menuBar: { menus, onAction, style:
137
+ 'hamburger' }`, and switch later through `shell.chrome.menuBar.setStyle(…)`.
138
+ A hamburger belongs in the top bar itself (at the left, before the app's
139
+ icon), not in the `.twm-global-menu-bar` row below.
140
+
104
141
  ## The menu bar in its own row under the top bar (0.17.13)
105
142
 
106
143
  A shell's menu bar can sit on a second line, as a full-width strip directly
@@ -120,7 +120,8 @@ background, a left border in the severity's colour (`--color-success`,
120
120
  `--color-warning`, `--color-danger`; info keeps the blue) and the entry
121
121
  animation `twm-notification-in`, which the sheet now defines. The card sets
122
122
  `pointer-events: auto` over the root's `none`, so its buttons and its
123
- click-to-close work.
123
+ click-to-close work. Since [Copy message](#copy-message), click-to-close takes
124
+ only a click on the card outside its text.
124
125
 
125
126
  Until 0.17.2 the card carried the bare `notification` / `notification--<severity>`
126
127
  classes, which no FlexDesk sheet styled: the card had no background and no
@@ -156,6 +157,27 @@ Until 0.17.3 the bell counted only on bus events, so a toast raised through
156
157
  was pushed as a second entry: a toast that timed out was in `history` twice
157
158
  and counted twice. `tests/notification_history_follows_center.test.mjs` holds it.
158
159
 
160
+ ### Copy message
161
+
162
+ Right-clicking a toast, or its entry in the `NotificationHistory` popover, opens
163
+ the context menu (`showContextMenu`) with **Copy message**. It copies the
164
+ notification's title, message and description, one per line and skipping any
165
+ that is empty, as plain text through `navigator.clipboard.writeText`. The menu
166
+ replaces the browser's native one there. The history popover stays open while
167
+ its menu is used. Every context menu draws on `--z-context-menu` (tokens.css),
168
+ over toasts (`--z-toast`) and managed windows, so the menu opens over the toast
169
+ that was right-clicked.
170
+
171
+ The text of a toast and of a history entry (title, message, description,
172
+ status) is selectable: the sheets give it `user-select: text` over the body's
173
+ `none`. A click or double-click on a toast's text (title, message,
174
+ description, status) selects it and does not close the toast, nor does a click
175
+ that ends a selection inside it. Only a click on the card outside the text (the
176
+ header's empty space, the padding, the timestamp) closes it, besides the ×
177
+ button. `notificationText(notification)` returns the copied
178
+ text, and `showNotificationMenu(event, notification, logger)` opens the menu.
179
+ Both are exported from `widgets`. `tests/notification_copy.test.mjs` covers it.
180
+
159
181
  ### Alerts with history: a log, a pane, a tab host and a bell (0.14.0)
160
182
 
161
183
  A toast is the moment. The **alert log** keeps what came in until it is
@@ -10,6 +10,7 @@ The shell's left, right and bottom panels, and its virtual desktops. A kind says
10
10
  - [Panels keep the size you dragged them to (0.17.0)](#panels-keep-the-size-you-dragged-them-to-0170)
11
11
  - [A navigation panel you drive yourself: `mountNavPanel` (0.17.0)](#a-navigation-panel-you-drive-yourself-mountnavpanel-0170)
12
12
  - [Your desktops beside the app's (0.10.0)](#your-desktops-beside-the-apps-0100)
13
+ - [A page that opens on the desktop you are on (0.17.17)](#a-page-that-opens-on-the-desktop-you-are-on-01717)
13
14
  - [A saved layout that cannot be read (0.17.14)](#a-saved-layout-that-cannot-be-read-01714)
14
15
 
15
16
  ## Panels a page does not offer (0.17.0)
@@ -127,6 +128,27 @@ main pane; it used to replace that pane with the active tab alone. A target stil
127
128
  holding only its seed is replaced, as before. `dispose()` removes the bar, so a
128
129
  rebuilt shell paints a fresh one.
129
130
 
131
+ ## A page that opens on the desktop you are on (0.17.17)
132
+
133
+ A desktop is the tiles and panels arranged on it. A kind marked `single: true`
134
+ exists once in the app, so opening it from another desktop switches to the
135
+ desktop that has it. A kind marked `single: 'desktop'` exists once per desktop
136
+ instead: the top-nav button, `navigate` and `openInPrimary` reveal the
137
+ instance on the active desktop, or open one there when that desktop has none,
138
+ and never switch desktop.
139
+
140
+ ```js
141
+ kinds: {
142
+ model: { label: 'Model', isTopNav: true, single: 'desktop' },
143
+ }
144
+ ```
145
+
146
+ On desktop 2, clicking *Model* opens the model on desktop 2 even while
147
+ desktop 1 shows its own; clicking it again reveals desktop 2's. Each desktop's
148
+ instance is saved with its layout and restored there. The details, and what
149
+ counts as a window on a desktop, are in
150
+ [Shell and navigation](shell.md#a-kind-that-exists-once-per-desktop-single-desktop-01717).
151
+
130
152
  ## A saved layout that cannot be read (0.17.14)
131
153
 
132
154
  The shell saves its layout through the host's `state` under the key `desktops`
File without changes
File without changes
@@ -10,6 +10,7 @@ A complete first boot is in the [quickstart](../quickstart.md#hello-shell). This
10
10
 
11
11
  - [Opt-in shell features](#opt-in-shell-features)
12
12
  - [A kind that exists once: `single` (0.17.0)](#a-kind-that-exists-once-single-0170)
13
+ - [A kind that exists once per desktop: `single: 'desktop'` (0.17.17)](#a-kind-that-exists-once-per-desktop-single-desktop-01717)
13
14
  - [An open single instance takes new props: `navigate(props)` (0.17.2)](#an-open-single-instance-takes-new-props-navigateprops-0172)
14
15
  - [A link to a kind nobody registered (0.17.2)](#a-link-to-a-kind-nobody-registered-0172)
15
16
  - [A pane that names what it shows: `ctx.setTitle` (0.12.1)](#a-pane-that-names-what-it-shows-ctxsettitle-0121)
@@ -74,6 +75,59 @@ with it. The taxonomy answers `isSingle(kind)`, `instanceKey(kind, props)` and
74
75
  `singleReason(kind)`; the window manager answers `isSingleKind(kind)`,
75
76
  `findInstance(kind, props)` and `revealInstance(kind, props)`.
76
77
 
78
+ ## A kind that exists once per desktop: `single: 'desktop'` (0.17.17)
79
+
80
+ A virtual desktop is the tiles and panels the user arranged on it, so a page
81
+ the user opens from the top nav belongs on the desktop they are looking at,
82
+ not on another desktop that already shows it. Mark such a kind
83
+ `single: 'desktop'`: at most one instance per desktop.
84
+
85
+ ```js
86
+ createTaxonomy({
87
+ root: 'cosmos',
88
+ kinds: {
89
+ cosmos: { label: 'Cosmos', isTopNav: true },
90
+ chart: { label: 'Star chart', isTopNav: true, single: 'desktop' }, // one per desktop
91
+ },
92
+ });
93
+ ```
94
+
95
+ - **Every open path looks only at the active desktop** — `navigate`, the top
96
+ nav, `openInPrimary`, `openInTab*`, `splitLeafWith`, Back. When the desktop
97
+ has the instance (in a tile, in a top-nav page a tile switched away from, or
98
+ in a window on it) it is revealed there, as for `single: true`; when it has
99
+ none, one is opened there. The open never switches desktop, and the
100
+ instances on other desktops are left as they are.
101
+ - **A window is on the desktop it was opened on** or moved to. A window
102
+ contained to a pane (`promoteInPlace`) is shown on that desktop only; a
103
+ free-floating one (`navigate(kind, props, { dest: 'window' })`) is drawn over
104
+ every desktop but is still the instance of the one it was opened on, never
105
+ of another: on desktop 1, *Model* reveals desktop 1's own model even while
106
+ desktop 2's floats in front of it.
107
+ - **The copy verbs refuse** within the desktop, as for `single: true`.
108
+ - **The move verbs refuse** to carry the kind onto a desktop that already holds
109
+ it: *Move to desktop N* (Alt+Shift+N and the tile and window menus, whose row
110
+ is greyed with the reason), and dropping a floating window onto another
111
+ desktop's tile. `wm.desktopMoveRefusal({ leafId } | { windowId }, idx)`
112
+ answers that reason, or null. Removing a desktop brings its windows across
113
+ to the active desktop and never closes one, so that desktop can then hold
114
+ the kind twice, until a reload keeps the first.
115
+ - **Navigating inside a window looks on the window's own desktop**
116
+ (`openInWindow`, and the in-window navigation that goes through it): a
117
+ floating window of desktop 1 navigated to the kind while desktop 2 is on
118
+ screen reveals desktop 1's instance, and shows the kind itself when desktop
119
+ 1 has none. The window stays on its desktop.
120
+ - **A page restore and a restored layout dedupe per desktop**: a tile's archived
121
+ page brings its instance back unless that desktop has it live elsewhere, and
122
+ a reload keeps the first instance on each desktop. Each desktop's instance
123
+ survives, live or archived.
124
+
125
+ `single: true` keeps its meaning: one instance in the app, and opening it
126
+ switches to the desktop it is on. Because `'desktop'` names this mode, it is
127
+ not read as a prop name. `findInstance(kind)` answers the active desktop's
128
+ instance; the taxonomy answers `isSinglePerDesktop(kind)`, and
129
+ `instanceKey(kind, props)` is the same for every props.
130
+
77
131
  ## An open single instance takes new props: `navigate(props)` (0.17.2)
78
132
 
79
133
  Revealing a `single` instance used to drop the props the open asked for: the
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/editor.js CHANGED
File without changes
package/flow.js CHANGED
File without changes
package/host.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flexdesk",
3
- "version": "0.17.16",
3
+ "version": "0.17.18",
4
4
  "description": "A tiling window manager and widget set for desktop-class web applications. Zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "repository": {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/src/host/host.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -53,15 +53,17 @@
53
53
  * not inserted and their toggles are disabled, showing
54
54
  * `reason`.
55
55
  *
56
- * single? `true`, or the name of a prop. The kind exists ONCE: content
57
- * that holds heavy app-wide state and must not be mounted
58
- * twice. With `true` there is one instance of the kind; with a
59
- * prop name there is one per value of that prop (`'id'`: one
60
- * tile per record). The window manager makes every open path
61
- * reveal the instance that exists, refuses the copy verbs with
62
- * a reason, and keeps the first of any duplicates a restored
63
- * layout holds. The root kind cannot be single: every new
64
- * pane, tab and desktop is seeded with it.
56
+ * single? `true`, `'desktop'`, or the name of a prop. The kind exists
57
+ * ONCE: content that holds heavy app-wide state and must not be
58
+ * mounted twice. With `true` there is one instance of the kind;
59
+ * with `'desktop'` there is one per virtual desktop, found and
60
+ * opened on the active one (so `'desktop'` is not a prop name
61
+ * here); with a prop name there is one per value of that prop
62
+ * (`'id'`: one tile per record). The window manager makes every
63
+ * open path reveal the instance that exists, refuses the copy
64
+ * verbs with a reason, and keeps the first of any duplicates a
65
+ * restored layout holds. The root kind cannot be single: every
66
+ * new pane, tab and desktop is seeded with it.
65
67
  *
66
68
  * window? `{ width?, height? }` — the initial size, in px, of a managed
67
69
  * window this kind opens in (`navigate(…, { dest: 'window' })`,
@@ -249,14 +251,19 @@ export function createTaxonomy({ kinds, root } = {}) {
249
251
  return s === true || (typeof s === 'string' && s !== '');
250
252
  };
251
253
 
254
+ /** Whether `kind` exists once per virtual desktop (`single: 'desktop'`)
255
+ * rather than once in the app. */
256
+ const isSinglePerDesktop = (kind) => _kinds[kind]?.single === 'desktop';
257
+
252
258
  /** The identity of one instance of a single kind: two tab specs with the
253
- * same key are the same instance. `null` for a kind that is not single,
254
- * which is how callers ask both questions at once. Keyed by the kind and,
255
- * for `single: '<prop>'`, that prop's value as a string. */
259
+ * same key are the same instance (on one desktop, for
260
+ * `single: 'desktop'`). `null` for a kind that is not single, which is
261
+ * how callers ask both questions at once. Keyed by the kind and, for
262
+ * `single: '<prop>'`, that prop's value as a string. */
256
263
  const instanceKey = (kind, props) => {
257
264
  if (!isSingle(kind)) return null;
258
265
  const by = _kinds[kind].single;
259
- if (by === true) return String(kind);
266
+ if (by === true || by === 'desktop') return String(kind);
260
267
  const v = props?.[by];
261
268
  return `${kind}\u0000${v == null ? '' : String(v)}`;
262
269
  };
@@ -287,6 +294,7 @@ export function createTaxonomy({ kinds, root } = {}) {
287
294
  panelOffer,
288
295
  topNavEntries,
289
296
  isSingle,
297
+ isSinglePerDesktop,
290
298
  instanceKey,
291
299
  singleReason,
292
300
  isWindowOnly,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -712,8 +712,11 @@ function _tileContextMenu(wm, leafId, x, y) {
712
712
  // `isPanel` above) never a panel: `moveFocusedToDesktop` refuses all
713
713
  // three too, because the keyboard reaches it without this menu.
714
714
  if (i === wm.desktops.activeIdx || d.fixed) continue;
715
+ // A desktop holds a `single: 'desktop'` kind once: greyed, saying so.
716
+ const refusal = wm.desktopMoveRefusal({ leafId }, i);
715
717
  items.push({ label: `Move to desktop ${d.label}`, icon: 'sweep',
716
- action: `move:${i}` });
718
+ action: `move:${i}`, disabled: !!refusal,
719
+ title: refusal || undefined });
717
720
  }
718
721
  }
719
722
  items.push({ separator: true });
File without changes
File without changes
File without changes