@formicoidea/labre-core 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/architecture/code.c4 +37 -34
  2. package/architecture/likec4.config.json +3 -3
  3. package/dist/_pkgs/affine-block-surface/extensions/reading-toolbar.js +12 -12
  4. package/dist/_pkgs/affine-components/context-menu/menu-renderer.d.ts +3 -1
  5. package/dist/_pkgs/affine-components/context-menu/menu-renderer.js +9 -1
  6. package/dist/_pkgs/affine-components/context-menu/menu.d.ts +7 -0
  7. package/dist/_pkgs/affine-components/context-menu/menu.js +5 -0
  8. package/dist/_pkgs/affine-components/context-menu/sub-menu.d.ts +16 -1
  9. package/dist/_pkgs/affine-components/context-menu/sub-menu.js +32 -5
  10. package/dist/_pkgs/affine-components/highlight-dropdown-menu/highlight-duotone-icon.js +8 -8
  11. package/dist/_pkgs/affine-components/highlight-dropdown-menu/text-duotone-icon.js +11 -11
  12. package/dist/_pkgs/affine-database-block/configs/slash-menu.js +14 -6
  13. package/dist/_pkgs/affine-database-block/database-block-styles.js +4 -3
  14. package/dist/_pkgs/affine-database-block/database-block.js +6 -18
  15. package/dist/_pkgs/affine-database-block/database-dnd-preview-block.js +5 -1
  16. package/dist/_pkgs/affine-database-block/detail-panel/block-renderer.js +5 -2
  17. package/dist/_pkgs/affine-database-block/detail-panel/note-renderer.js +3 -2
  18. package/dist/_pkgs/affine-database-block/external-data-source.d.ts +10 -0
  19. package/dist/_pkgs/affine-database-block/external-data-source.js +24 -0
  20. package/dist/_pkgs/affine-database-block/properties/created-time/define.js +3 -1
  21. package/dist/_pkgs/affine-database-block/properties/link/cell-renderer.js +3 -2
  22. package/dist/_pkgs/affine-database-block/properties/link/define.js +3 -1
  23. package/dist/_pkgs/affine-database-block/properties/rich-text/define.js +3 -1
  24. package/dist/_pkgs/affine-database-block/properties/text/hosted.js +1 -0
  25. package/dist/_pkgs/affine-database-block/properties/title/define.js +3 -1
  26. package/dist/_pkgs/affine-database-block/properties/title/hosted.js +1 -0
  27. package/dist/_pkgs/affine-database-block/service/new-row-menu.d.ts +1 -1
  28. package/dist/_pkgs/affine-database-block/service/new-row-menu.js +1 -1
  29. package/dist/_pkgs/affine-database-block/translations.d.ts +11 -0
  30. package/dist/_pkgs/affine-database-block/translations.js +57 -0
  31. package/dist/_pkgs/affine-database-block/views/index.d.ts +7 -9
  32. package/dist/_pkgs/affine-database-block/views/index.js +7 -10
  33. package/dist/_pkgs/affine-database-engine/core/common/properties.js +7 -7
  34. package/dist/_pkgs/affine-database-engine/core/common/property-menu.d.ts +18 -2
  35. package/dist/_pkgs/affine-database-engine/core/common/property-menu.js +78 -27
  36. package/dist/_pkgs/affine-database-engine/core/data-source/base.d.ts +11 -0
  37. package/dist/_pkgs/affine-database-engine/core/data-source/base.js +3 -0
  38. package/dist/_pkgs/affine-database-engine/core/detail/detail.js +8 -5
  39. package/dist/_pkgs/affine-database-engine/core/detail/field.js +14 -7
  40. package/dist/_pkgs/affine-database-engine/core/filter/add-filter.js +4 -2
  41. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/boolean.js +7 -4
  42. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/create.d.ts +9 -1
  43. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/create.js +2 -0
  44. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/date.js +7 -4
  45. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/multi-tag.js +17 -8
  46. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/number.js +6 -6
  47. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/string.js +19 -12
  48. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/tag.js +9 -4
  49. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/unknown.js +7 -4
  50. package/dist/_pkgs/affine-database-engine/core/filter/literal/define.js +6 -4
  51. package/dist/_pkgs/affine-database-engine/core/filter/literal/matcher.d.ts +2 -1
  52. package/dist/_pkgs/affine-database-engine/core/filter/literal/matcher.js +3 -2
  53. package/dist/_pkgs/affine-database-engine/core/filter/literal/types.d.ts +4 -1
  54. package/dist/_pkgs/affine-database-engine/core/filter/literal/types.js +1 -1
  55. package/dist/_pkgs/affine-database-engine/core/group-by/define.js +11 -9
  56. package/dist/_pkgs/affine-database-engine/core/group-by/group-title.js +16 -6
  57. package/dist/_pkgs/affine-database-engine/core/group-by/locate-row.d.ts +16 -0
  58. package/dist/_pkgs/affine-database-engine/core/group-by/locate-row.js +32 -0
  59. package/dist/_pkgs/affine-database-engine/core/group-by/renderer/date-group.js +10 -3
  60. package/dist/_pkgs/affine-database-engine/core/group-by/setting.d.ts +2 -0
  61. package/dist/_pkgs/affine-database-engine/core/group-by/setting.js +40 -28
  62. package/dist/_pkgs/affine-database-engine/core/group-by/trait.d.ts +3 -0
  63. package/dist/_pkgs/affine-database-engine/core/group-by/trait.js +8 -0
  64. package/dist/_pkgs/affine-database-engine/core/group-by/types.d.ts +3 -1
  65. package/dist/_pkgs/affine-database-engine/core/group-by/types.js +1 -1
  66. package/dist/_pkgs/affine-database-engine/core/property/types.d.ts +7 -0
  67. package/dist/_pkgs/affine-database-engine/core/property/types.js +1 -1
  68. package/dist/_pkgs/affine-database-engine/core/sort/add-sort.js +3 -1
  69. package/dist/_pkgs/affine-database-engine/core/statistics/any.js +43 -21
  70. package/dist/_pkgs/affine-database-engine/core/statistics/checkbox.js +29 -14
  71. package/dist/_pkgs/affine-database-engine/core/statistics/number.js +54 -31
  72. package/dist/_pkgs/affine-database-engine/core/statistics/types.d.ts +8 -0
  73. package/dist/_pkgs/affine-database-engine/core/statistics/types.js +2 -1
  74. package/dist/_pkgs/affine-database-engine/core/utils/wording.d.ts +20 -1
  75. package/dist/_pkgs/affine-database-engine/core/utils/wording.js +24 -0
  76. package/dist/_pkgs/affine-database-engine/core/view-manager/property.d.ts +7 -0
  77. package/dist/_pkgs/affine-database-engine/core/view-manager/property.js +29 -5
  78. package/dist/_pkgs/affine-database-engine/core/view-manager/single-view.d.ts +14 -0
  79. package/dist/_pkgs/affine-database-engine/core/view-manager/single-view.js +11 -0
  80. package/dist/_pkgs/affine-database-engine/property-presets/checkbox/define.js +3 -1
  81. package/dist/_pkgs/affine-database-engine/property-presets/date/define.js +3 -1
  82. package/dist/_pkgs/affine-database-engine/property-presets/multi-select/define.js +3 -1
  83. package/dist/_pkgs/affine-database-engine/property-presets/number/define.js +3 -1
  84. package/dist/_pkgs/affine-database-engine/property-presets/number/utils/formats.d.ts +3 -0
  85. package/dist/_pkgs/affine-database-engine/property-presets/number/utils/formats.js +56 -9
  86. package/dist/_pkgs/affine-database-engine/property-presets/progress/define.js +3 -1
  87. package/dist/_pkgs/affine-database-engine/property-presets/select/define.js +3 -1
  88. package/dist/_pkgs/affine-database-engine/property-presets/text/define.js +3 -1
  89. package/dist/_pkgs/affine-database-engine/translations.d.ts +133 -5
  90. package/dist/_pkgs/affine-database-engine/translations.js +625 -9
  91. package/dist/_pkgs/affine-database-engine/view-presets/convert.js +0 -28
  92. package/dist/_pkgs/affine-database-engine/view-presets/effect.js +0 -2
  93. package/dist/_pkgs/affine-database-engine/view-presets/flat/flat-converts.js +2 -4
  94. package/dist/_pkgs/affine-database-engine/view-presets/flat/flat-view-ui.d.ts +57 -0
  95. package/dist/_pkgs/affine-database-engine/view-presets/flat/flat-view-ui.js +113 -4
  96. package/dist/_pkgs/affine-database-engine/view-presets/gallery/gallery-view.js +2 -1
  97. package/dist/_pkgs/affine-database-engine/view-presets/index.d.ts +0 -2
  98. package/dist/_pkgs/affine-database-engine/view-presets/index.js +0 -3
  99. package/dist/_pkgs/affine-database-engine/view-presets/kanban/kanban-view-manager.js +2 -0
  100. package/dist/_pkgs/affine-database-engine/view-presets/kanban/mobile/group.js +7 -5
  101. package/dist/_pkgs/affine-database-engine/view-presets/kanban/mobile/kanban-view-ui-logic.d.ts +5 -0
  102. package/dist/_pkgs/affine-database-engine/view-presets/kanban/mobile/kanban-view-ui-logic.js +25 -3
  103. package/dist/_pkgs/affine-database-engine/view-presets/kanban/mobile/menu.js +12 -6
  104. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/controller/selection.d.ts +8 -0
  105. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/controller/selection.js +35 -12
  106. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/group.js +7 -5
  107. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/header.js +5 -1
  108. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/kanban-view-ui-logic.d.ts +5 -0
  109. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/kanban-view-ui-logic.js +8 -4
  110. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/menu.js +12 -6
  111. package/dist/_pkgs/affine-database-engine/view-presets/list/list-view.js +2 -1
  112. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/column-header.js +6 -4
  113. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/group.js +6 -5
  114. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/header.js +2 -2
  115. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/table-view-ui-logic.d.ts +4 -3
  116. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/table-view-ui-logic.js +29 -5
  117. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/controller/clipboard.js +6 -2
  118. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/controller/selection.js +2 -0
  119. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/group.js +6 -5
  120. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/header/column-header.js +11 -7
  121. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/header/database-header-column.js +16 -12
  122. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/header/number-format-bar.js +4 -2
  123. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/menu.js +9 -6
  124. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/table-view-ui-logic.d.ts +8 -1
  125. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/table-view-ui-logic.js +29 -7
  126. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/controller/clipboard.js +6 -2
  127. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/controller/selection.js +2 -0
  128. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/bottom/group-footer.js +5 -2
  129. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/bottom/stats/column-stats-column.d.ts +1 -1
  130. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/bottom/stats/column-stats-column.js +14 -6
  131. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/group-header.js +5 -2
  132. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/group-title.js +5 -3
  133. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/header/column-header.js +17 -10
  134. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/header/number-format-bar.js +4 -2
  135. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/header/single-column-header.js +20 -14
  136. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/row/menu.js +9 -6
  137. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/table-view-ui-logic.d.ts +6 -1
  138. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/table-view-ui-logic.js +30 -3
  139. package/dist/_pkgs/affine-database-engine/view-presets/table/stats/column-stats-column.d.ts +1 -1
  140. package/dist/_pkgs/affine-database-engine/view-presets/table/stats/column-stats-column.js +14 -6
  141. package/dist/_pkgs/affine-database-engine/view-presets/table/table-view-manager.js +2 -0
  142. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/filter/condition-view.d.ts +2 -0
  143. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/filter/condition-view.js +13 -6
  144. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/filter/group-panel-view.js +20 -13
  145. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/filter/root-panel-view.js +10 -5
  146. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/sort/root-panel.js +8 -5
  147. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/search/search.js +2 -2
  148. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/table-add-row/add-row.d.ts +6 -0
  149. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/table-add-row/add-row.js +49 -4
  150. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/view-options/view-settings.d.ts +3 -3
  151. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/view-options/view-settings.js +3 -3
  152. package/dist/_pkgs/affine-database-engine/widget-presets/views-bar/views-view.js +5 -0
  153. package/dist/_pkgs/affine-database-view-block/configs/slash-menu.js +6 -3
  154. package/dist/_pkgs/affine-database-view-block/data-view-block.js +5 -4
  155. package/dist/_pkgs/affine-fragment-doc-title/doc-title.d.ts +3 -2
  156. package/dist/_pkgs/affine-fragment-doc-title/doc-title.js +3 -2
  157. package/dist/_pkgs/affine-shared/services/data-source-picker-service.d.ts +31 -0
  158. package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/index.js +26 -26
  159. package/dist/_pkgs/affine-widget-keyboard-toolbar/icons.js +98 -98
  160. package/dist/_pkgs/affine-widget-page-dragging-area/index.js +10 -10
  161. package/dist/_pkgs/affine-widget-viewport-overlay/index.js +13 -13
  162. package/package.json +1 -1
  163. package/dist/_pkgs/affine-database-engine/view-presets/calendar/calendar-view-manager.d.ts +0 -70
  164. package/dist/_pkgs/affine-database-engine/view-presets/calendar/calendar-view-manager.js +0 -449
  165. package/dist/_pkgs/affine-database-engine/view-presets/calendar/define.d.ts +0 -10
  166. package/dist/_pkgs/affine-database-engine/view-presets/calendar/define.js +0 -29
  167. package/dist/_pkgs/affine-database-engine/view-presets/calendar/effect.d.ts +0 -1
  168. package/dist/_pkgs/affine-database-engine/view-presets/calendar/effect.js +0 -4
  169. package/dist/_pkgs/affine-database-engine/view-presets/calendar/index.d.ts +0 -6
  170. package/dist/_pkgs/affine-database-engine/view-presets/calendar/index.js +0 -6
  171. package/dist/_pkgs/affine-database-engine/view-presets/calendar/layout.d.ts +0 -37
  172. package/dist/_pkgs/affine-database-engine/view-presets/calendar/layout.js +0 -138
  173. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/actions.d.ts +0 -7
  174. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/actions.js +0 -64
  175. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/dnd.d.ts +0 -32
  176. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/dnd.js +0 -159
  177. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/effect.d.ts +0 -1
  178. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/effect.js +0 -7
  179. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/hit-test.d.ts +0 -1
  180. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/hit-test.js +0 -28
  181. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/styles.d.ts +0 -1
  182. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/styles.js +0 -707
  183. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/view.d.ts +0 -89
  184. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/view.js +0 -849
  185. package/dist/_pkgs/affine-database-engine/view-presets/calendar/renderer.d.ts +0 -2
  186. package/dist/_pkgs/affine-database-engine/view-presets/calendar/renderer.js +0 -8
  187. package/dist/_pkgs/affine-database-engine/view-presets/calendar/source.d.ts +0 -8
  188. package/dist/_pkgs/affine-database-engine/view-presets/calendar/source.js +0 -3
  189. package/dist/_pkgs/affine-database-engine/view-presets/calendar/types.d.ts +0 -79
  190. package/dist/_pkgs/affine-database-engine/view-presets/calendar/types.js +0 -1
@@ -38,6 +38,7 @@ import { ShadowlessElement } from '../../std/index.js';
38
38
  import { cssVarV2 } from '@toeverything/theme/v2';
39
39
  import { css, html, unsafeCSS } from 'lit';
40
40
  import { property } from 'lit/decorators.js';
41
+ import { DOC_UNTITLED, translateKey } from '../../affine-shared/services/index.js';
41
42
  let BlockRenderer = (() => {
42
43
  let _classSuper = WithDisposable(ShadowlessElement);
43
44
  let _host_decorators;
@@ -87,8 +88,8 @@ let BlockRenderer = (() => {
87
88
  line-height: 24px;
88
89
  }
89
90
 
90
- database-datasource-block-renderer.empty rich-text::before {
91
- content: 'Untitled';
91
+ database-datasource-block-renderer.empty[data-untitled] rich-text::before {
92
+ content: attr(data-untitled);
92
93
  position: absolute;
93
94
  color: var(--affine-text-disable-color);
94
95
  font-size: 15px;
@@ -159,6 +160,8 @@ let BlockRenderer = (() => {
159
160
  if (!model) {
160
161
  return;
161
162
  }
163
+ // Read by the empty title's placeholder (`::before`), in the host's words.
164
+ this.dataset.untitled = translateKey(this.host.std, ...DOC_UNTITLED);
162
165
  return html `
163
166
  ${this.renderIcon()}
164
167
  <rich-text
@@ -34,7 +34,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
34
34
  };
35
35
  import { CodeBlockModel, ListBlockModel, ParagraphBlockModel, } from '../../affine-model/index.js';
36
36
  import { REFERENCE_NODE } from '../../affine-shared/consts/index.js';
37
- import { TelemetryProvider } from '../../affine-shared/services/index.js';
37
+ import { TelemetryProvider, translateKey } from '../../affine-shared/services/index.js';
38
38
  import { createDefaultDoc, matchModels } from '../../affine-shared/utils/index.js';
39
39
  import { SignalWatcher, WithDisposable } from '../../global/lit/index.js';
40
40
  import { ShadowlessElement } from '../../std/index.js';
@@ -43,6 +43,7 @@ import { cssVarV2 } from '@toeverything/theme/v2';
43
43
  import { css, html, unsafeCSS } from 'lit';
44
44
  import { property } from 'lit/decorators.js';
45
45
  import { isPureText } from '../utils/title-doc.js';
46
+ import { DATABASE_DETAIL_EMPTY_HINT } from '../translations.js';
46
47
  let NoteRenderer = (() => {
47
48
  let _classSuper = SignalWatcher(WithDisposable(ShadowlessElement));
48
49
  let _rowId_decorators;
@@ -138,7 +139,7 @@ let NoteRenderer = (() => {
138
139
  @click="${this.addNote}"
139
140
  style="max-width: var(--affine-editor-width);margin: auto;cursor: pointer;color: var(--affine-text-disable-color)"
140
141
  >
141
- Click to create a linked doc in center peek.
142
+ ${translateKey(this.host.std, ...DATABASE_DETAIL_EMPTY_HINT)}
142
143
  </div>
143
144
  </div>`;
144
145
  }
@@ -121,6 +121,16 @@ export declare abstract class HostedDataSourceBase extends DataSourceBase {
121
121
  * `readonly$` (cells, paste, drag, "New", view and property menus).
122
122
  */
123
123
  readonly locked$: import("@preact/signals-core").Signal<boolean>;
124
+ /**
125
+ * The host's lock on the current view's source (`sourcePolicy` of the
126
+ * picker, ADR 0035 amendment of 2026-10-29), asked again only when the
127
+ * block's database or the view's source changes; the host's signal then
128
+ * carries every flip to the open views. `null`: nothing locked (a native
129
+ * database, no picker, no `sourcePolicy`, or a host that threw).
130
+ */
131
+ private readonly hostPolicy$;
132
+ readonly propertiesLocked$: ReadonlySignal<boolean>;
133
+ readonly rowsLocked$: ReadonlySignal<boolean>;
124
134
  readonly$: ReadonlySignal<boolean>;
125
135
  viewConverts: import("./index.js").ViewConvertConfig[];
126
136
  viewMetas: ViewMeta[];
@@ -95,6 +95,30 @@ export class HostedDataSourceBase extends DataSourceBase {
95
95
  * `readonly$` (cells, paste, drag, "New", view and property menus).
96
96
  */
97
97
  this.locked$ = signal(false);
98
+ /**
99
+ * The host's lock on the current view's source (`sourcePolicy` of the
100
+ * picker, ADR 0035 amendment of 2026-10-29), asked again only when the
101
+ * block's database or the view's source changes; the host's signal then
102
+ * carries every flip to the open views. `null`: nothing locked (a native
103
+ * database, no picker, no `sourcePolicy`, or a host that threw).
104
+ */
105
+ this.hostPolicy$ = computed(() => {
106
+ const databaseId = this._model.props.externalSourceId$.value;
107
+ if (!databaseId)
108
+ return null;
109
+ const picker = this.serviceGet(DataSourcePickerProvider);
110
+ if (!picker?.sourcePolicy)
111
+ return null;
112
+ try {
113
+ return picker.sourcePolicy(databaseId, this.currentSourceId$.value);
114
+ }
115
+ catch (error) {
116
+ console.error(error);
117
+ return null;
118
+ }
119
+ });
120
+ this.propertiesLocked$ = computed(() => this.hostPolicy$.value?.value.propertiesLocked === true);
121
+ this.rowsLocked$ = computed(() => this.hostPolicy$.value?.value.rowsLocked === true);
98
122
  // Mobile edits too (ADR 0035 §1, B5): the table and kanban presets ship
99
123
  // their own touch renderers, chosen by `DataViewRootUILogic`.
100
124
  this.readonly$ = computed(() => {
@@ -1,9 +1,11 @@
1
1
  import { propertyType, t } from '../../../affine-database-engine/index.js';
2
2
  import { format } from 'date-fns/format';
3
3
  import zod from 'zod';
4
+ import { DATABASE_TYPE_CREATED_TIME } from '../../translations.js';
4
5
  export const createdTimeColumnType = propertyType('created-time');
5
6
  export const createdTimePropertyModelConfig = createdTimeColumnType.modelConfig({
6
- name: 'Created Time',
7
+ name: DATABASE_TYPE_CREATED_TIME[1],
8
+ nameWording: DATABASE_TYPE_CREATED_TIME,
7
9
  propertyData: {
8
10
  schema: zod.object({}),
9
11
  default: () => ({}),
@@ -1,5 +1,5 @@
1
1
  import { RefNodeSlotsProvider } from '../../../affine-inline-reference/index.js';
2
- import { ParseDocUrlProvider } from '../../../affine-shared/services/index.js';
2
+ import { ParseDocUrlProvider, DOC_UNTITLED, translateKey, } from '../../../affine-shared/services/index.js';
3
3
  import { isSafeLinkUrl, isValidUrl, normalizeUrl, stopPropagation, } from '../../../affine-shared/utils/index.js';
4
4
  import { BaseCellRenderer, createFromBaseCellRenderer, createIcon, } from '../../../affine-database-engine/index.js';
5
5
  import { EditIcon } from '@blocksuite/icons/lit';
@@ -68,7 +68,8 @@ export class LinkCell extends BaseCellRenderer {
68
68
  if (title == null) {
69
69
  return;
70
70
  }
71
- return title || 'Untitled';
71
+ return (title ||
72
+ (this.std ? translateKey(this.std, ...DOC_UNTITLED) : DOC_UNTITLED[1]));
72
73
  });
73
74
  }
74
75
  firstUpdated(_changedProperties) {
@@ -1,8 +1,10 @@
1
1
  import { propertyType, t } from '../../../affine-database-engine/index.js';
2
2
  import zod from 'zod';
3
+ import { DATABASE_TYPE_LINK } from '../../translations.js';
3
4
  export const linkColumnType = propertyType('link');
4
5
  export const linkPropertyModelConfig = linkColumnType.modelConfig({
5
- name: 'Link',
6
+ name: DATABASE_TYPE_LINK[1],
7
+ nameWording: DATABASE_TYPE_LINK,
6
8
  propertyData: {
7
9
  schema: zod.object({}),
8
10
  default: () => ({}),
@@ -4,6 +4,7 @@ import * as Y from 'yjs';
4
4
  import zod from 'zod';
5
5
  import { EditorHostKey } from '../../context/host-context.js';
6
6
  import { isLinkedDoc } from '../../utils/title-doc.js';
7
+ import { DATABASE_TYPE_TEXT } from '../../translations.js';
7
8
  export const richTextColumnType = propertyType('rich-text');
8
9
  export const toYText = (text) => {
9
10
  if (text instanceof Text) {
@@ -12,7 +13,8 @@ export const toYText = (text) => {
12
13
  return text;
13
14
  };
14
15
  export const richTextPropertyModelConfig = richTextColumnType.modelConfig({
15
- name: 'Text',
16
+ name: DATABASE_TYPE_TEXT[1],
17
+ nameWording: DATABASE_TYPE_TEXT,
16
18
  propertyData: {
17
19
  schema: zod.object({}),
18
20
  default: () => ({}),
@@ -19,6 +19,7 @@ export const hostedTextColumnConfig = {
19
19
  config: {
20
20
  ...textPropertyConfig.config,
21
21
  name: richTextPropertyModelConfig.config.name,
22
+ nameWording: richTextPropertyModelConfig.config.nameWording,
22
23
  hide: false,
23
24
  },
24
25
  };
@@ -4,9 +4,11 @@ import { Doc } from 'yjs';
4
4
  import zod from 'zod';
5
5
  import { EditorHostKey } from '../../context/host-context.js';
6
6
  import { isLinkedDoc } from '../../utils/title-doc.js';
7
+ import { DATABASE_TYPE_TITLE } from '../../translations.js';
7
8
  export const titleColumnType = propertyType('title');
8
9
  export const titlePropertyModelConfig = titleColumnType.modelConfig({
9
- name: 'Title',
10
+ name: DATABASE_TYPE_TITLE[1],
11
+ nameWording: DATABASE_TYPE_TITLE,
10
12
  propertyData: {
11
13
  schema: zod.object({}),
12
14
  default: () => ({}),
@@ -18,6 +18,7 @@ import { titleColumnType, titlePropertyModelConfig } from './define.js';
18
18
  export const hostedTitlePropertyModelConfig = titleColumnType.modelConfig({
19
19
  // One wording and one data shape with the inline title column.
20
20
  name: titlePropertyModelConfig.config.name,
21
+ nameWording: titlePropertyModelConfig.config.nameWording,
21
22
  propertyData: titlePropertyModelConfig.config.propertyData,
22
23
  jsonValue: {
23
24
  schema: zod.string(),
@@ -41,7 +41,7 @@ export declare const DatabaseNewRowMenuProvider: import("../../global/di/types.j
41
41
  * say it does not.
42
42
  *
43
43
  * Absent or `null`, "New" is the single button of 0.48 in every view (table,
44
- * kanban, gallery, list, calendar) and on the full page: no chevron.
44
+ * kanban, gallery, list) and on the full page: no chevron.
45
45
  *
46
46
  * `di.override`, so a later registration wins and registering twice never
47
47
  * throws at boot.
@@ -5,7 +5,7 @@ export const DatabaseNewRowMenuProvider = createIdentifier('LabreDatabaseNewRowM
5
5
  * say it does not.
6
6
  *
7
7
  * Absent or `null`, "New" is the single button of 0.48 in every view (table,
8
- * kanban, gallery, list, calendar) and on the full page: no chevron.
8
+ * kanban, gallery, list) and on the full page: no chevron.
9
9
  *
10
10
  * `di.override`, so a later registration wins and registering twice never
11
11
  * throws at boot.
@@ -25,4 +25,15 @@ export declare const DATABASE_VALUES_LOADING: ChromeWording;
25
25
  export declare const DATABASE_VALUES_ERROR: ChromeWording;
26
26
  /** The chevron beside "New", as its label and tooltip. */
27
27
  export declare const DATABASE_NEW_RECORD_MORE: ChromeWording;
28
+ export declare const DATABASE_TYPE_CREATED_TIME: ChromeWording;
29
+ export declare const DATABASE_TYPE_LINK: ChromeWording;
30
+ export declare const DATABASE_TYPE_TEXT: ChromeWording;
31
+ export declare const DATABASE_TYPE_TITLE: ChromeWording;
32
+ export declare const DATABASE_SLASH_TABLE_DESCRIPTION: ChromeWording;
33
+ export declare const DATABASE_SLASH_KANBAN_DESCRIPTION: ChromeWording;
34
+ export declare const DATABASE_TITLE_LABEL: ChromeWording;
35
+ export declare const DATABASE_DELETE: ChromeWording;
36
+ export declare const DATABASE_DRAG_PREVIEW: ChromeWording;
37
+ export declare const DATABASE_DETAIL_EMPTY_HINT: ChromeWording;
38
+ export declare const DATABASE_SLASH_TODO: ChromeWording;
28
39
  export declare const DATABASE_WORDINGS: readonly ChromeWording[];
@@ -66,6 +66,52 @@ export const DATABASE_NEW_RECORD_MORE = [
66
66
  'com.labre.database.new-record.more',
67
67
  'Templates and options',
68
68
  ];
69
+ /* ── Database chrome keyed at the recette of 1.1 (#572) ── */
70
+ export const DATABASE_TYPE_CREATED_TIME = [
71
+ 'com.labre.database.property-type.created-time',
72
+ 'Created Time',
73
+ ];
74
+ export const DATABASE_TYPE_LINK = [
75
+ 'com.labre.database.property-type.link',
76
+ 'Link',
77
+ ];
78
+ export const DATABASE_TYPE_TEXT = [
79
+ 'com.labre.database.property-type.text',
80
+ 'Text',
81
+ ];
82
+ export const DATABASE_TYPE_TITLE = [
83
+ 'com.labre.database.property-type.title',
84
+ 'Title',
85
+ ];
86
+ export const DATABASE_SLASH_TABLE_DESCRIPTION = [
87
+ 'com.labre.database.slash.table.description',
88
+ 'Display items in a table format.',
89
+ ];
90
+ export const DATABASE_SLASH_KANBAN_DESCRIPTION = [
91
+ 'com.labre.database.slash.kanban.description',
92
+ 'Visualize data in a dashboard.',
93
+ ];
94
+ export const DATABASE_TITLE_LABEL = [
95
+ 'com.labre.database.title.label',
96
+ 'Database title',
97
+ ];
98
+ export const DATABASE_DELETE = [
99
+ 'com.labre.database.delete',
100
+ 'Delete Database',
101
+ ];
102
+ export const DATABASE_DRAG_PREVIEW = [
103
+ 'com.labre.database.drag-preview',
104
+ 'Database Block',
105
+ ];
106
+ export const DATABASE_DETAIL_EMPTY_HINT = [
107
+ 'com.labre.database.detail.empty-hint',
108
+ 'Click to create a linked doc in center peek.',
109
+ ];
110
+ /* ── The data-view block's slash entry ── */
111
+ export const DATABASE_SLASH_TODO = [
112
+ 'com.labre.database.slash.todo',
113
+ 'Todo',
114
+ ];
69
115
  export const DATABASE_WORDINGS = [
70
116
  DATABASE_CREATION_TITLE,
71
117
  DATABASE_MOVE_INTO_SOURCE,
@@ -79,4 +125,15 @@ export const DATABASE_WORDINGS = [
79
125
  DATABASE_TURN_INTO_NESTED,
80
126
  DATABASE_VALUES_ERROR,
81
127
  DATABASE_VALUES_LOADING,
128
+ DATABASE_TYPE_CREATED_TIME,
129
+ DATABASE_TYPE_LINK,
130
+ DATABASE_TYPE_TEXT,
131
+ DATABASE_TYPE_TITLE,
132
+ DATABASE_SLASH_TABLE_DESCRIPTION,
133
+ DATABASE_SLASH_KANBAN_DESCRIPTION,
134
+ DATABASE_TITLE_LABEL,
135
+ DATABASE_DELETE,
136
+ DATABASE_DRAG_PREVIEW,
137
+ DATABASE_DETAIL_EMPTY_HINT,
138
+ DATABASE_SLASH_TODO,
82
139
  ];
@@ -1,17 +1,16 @@
1
1
  import type { ViewMeta } from '../../affine-database-engine/index.js';
2
2
  /**
3
3
  * Every preset a stored `affine:database` view can be drawn with (ADR 0035
4
- * §6). Calendar stays here: calendar views were creatable until 0.48, so
5
- * documents hold them, and a stored calendar keeps rendering and editing as a
6
- * calendar. Never shrink this list: a mode that leaves it falls back to a
7
- * table on every document that stores it.
4
+ * §6). A mode that leaves this list falls back to a table on every document
5
+ * that stores it, so shrinking it is a major and an ADR decision: calendar
6
+ * left it in 2.0 (ADR 0035 amendment of 2026-10-29), and a stored calendar
7
+ * now draws as a table through `databaseBlockViewMetaGet`.
8
8
  */
9
9
  export declare const databaseBlockRenderableViews: ViewMeta[];
10
10
  /**
11
11
  * The presets a user can CREATE, a subset of the renderable ones: what the
12
12
  * data source answers as `viewMetas`, so the views bar ("+", "Create …") and
13
- * the Layout switch list only these. Calendar left it after 0.48 (ADR 0035 §6,
14
- * labre ADR-0046 §8): no new calendar, the stored ones still render.
13
+ * the Layout switch list only these.
15
14
  */
16
15
  export declare const databaseBlockViews: ViewMeta[];
17
16
  /** The preset a mode with none of its own is drawn with. */
@@ -26,8 +25,7 @@ export declare const databaseBlockViewMap: Record<string, ViewMeta | undefined>;
26
25
  */
27
26
  export declare function databaseBlockViewMetaGet(mode: string): ViewMeta;
28
27
  /**
29
- * Conversions INTO a creatable preset only: switching a view's layout to
30
- * calendar would create a calendar. The conversions out of calendar stay, so a
31
- * stored calendar can still become a table or a board.
28
+ * Conversions INTO a creatable preset only: a layout switch never creates a
29
+ * view a user could not have added.
32
30
  */
33
31
  export declare const databaseBlockViewConverts: import("../index.js").ViewConvertConfig[];
@@ -1,23 +1,21 @@
1
1
  import { viewConverts, viewPresets, } from '../../affine-database-engine/view-presets/index.js';
2
2
  /**
3
3
  * Every preset a stored `affine:database` view can be drawn with (ADR 0035
4
- * §6). Calendar stays here: calendar views were creatable until 0.48, so
5
- * documents hold them, and a stored calendar keeps rendering and editing as a
6
- * calendar. Never shrink this list: a mode that leaves it falls back to a
7
- * table on every document that stores it.
4
+ * §6). A mode that leaves this list falls back to a table on every document
5
+ * that stores it, so shrinking it is a major and an ADR decision: calendar
6
+ * left it in 2.0 (ADR 0035 amendment of 2026-10-29), and a stored calendar
7
+ * now draws as a table through `databaseBlockViewMetaGet`.
8
8
  */
9
9
  export const databaseBlockRenderableViews = [
10
10
  viewPresets.tableViewMeta,
11
11
  viewPresets.kanbanViewMeta,
12
- viewPresets.calendarViewMeta,
13
12
  viewPresets.galleryViewMeta,
14
13
  viewPresets.listViewMeta,
15
14
  ];
16
15
  /**
17
16
  * The presets a user can CREATE, a subset of the renderable ones: what the
18
17
  * data source answers as `viewMetas`, so the views bar ("+", "Create …") and
19
- * the Layout switch list only these. Calendar left it after 0.48 (ADR 0035 §6,
20
- * labre ADR-0046 §8): no new calendar, the stored ones still render.
18
+ * the Layout switch list only these.
21
19
  */
22
20
  export const databaseBlockViews = [
23
21
  viewPresets.tableViewMeta,
@@ -42,8 +40,7 @@ export function databaseBlockViewMetaGet(mode) {
42
40
  }
43
41
  const creatableModes = new Set(databaseBlockViews.map(view => view.type));
44
42
  /**
45
- * Conversions INTO a creatable preset only: switching a view's layout to
46
- * calendar would create a calendar. The conversions out of calendar stay, so a
47
- * stored calendar can still become a table or a board.
43
+ * Conversions INTO a creatable preset only: a layout switch never creates a
44
+ * view a user could not have added.
48
45
  */
49
46
  export const databaseBlockViewConverts = viewConverts.filter(convert => creatableModes.has(convert.to));
@@ -42,7 +42,7 @@ import { css, html, nothing, unsafeCSS } from 'lit';
42
42
  import { property, query } from 'lit/decorators.js';
43
43
  import { classMap } from 'lit/directives/class-map.js';
44
44
  import { repeat } from 'lit/directives/repeat.js';
45
- import { DATA_VIEW_NEW_PROPERTY, DATA_VIEW_PROPERTIES, DATA_VIEW_PROPERTY_TYPE, } from '../../translations.js';
45
+ import { DATA_VIEW_NEW_PROPERTY, DATA_VIEW_PROPERTIES, DATA_VIEW_PROPERTY_TYPE, DATA_VIEW_HIDE_ALL, DATA_VIEW_SHOW_ALL, } from '../../translations.js';
46
46
  import { dragHandler } from '../utils/wc-dnd/dnd-context.js';
47
47
  import { defaultActivators } from '../utils/wc-dnd/sensors/index.js';
48
48
  import { createSortContext, sortable, } from '../utils/wc-dnd/sort/sort-context.js';
@@ -214,8 +214,9 @@ let DataViewPropertiesSettingView = (() => {
214
214
  get view() { return this.#view_accessor_storage; }
215
215
  set view(value) { this.#view_accessor_storage = value; }
216
216
  _renderNewProperty() {
217
- if (this.view.readonly$.value)
217
+ if (this.view.readonly$.value || this.view.propertiesLocked$.value) {
218
218
  return nothing;
219
+ }
219
220
  return html `<div
220
221
  class="property-item property-item-new"
221
222
  data-testid="data-view-properties-new"
@@ -267,16 +268,15 @@ let DataViewPropertiesSettingView = (() => {
267
268
  this._openMenu = (property, row) => {
268
269
  if (this.view.readonly$.value)
269
270
  return;
270
- popPropertyMenu(popupTargetFromElement(row), property, {
271
- wording: this.wording,
272
- });
271
+ popPropertyMenu(popupTargetFromElement(row), property, { wording: this.wording }, { beside: true });
273
272
  };
274
273
  this._newProperty = (event) => {
275
- if (this.view.readonly$.value)
274
+ if (this.view.readonly$.value || this.view.propertiesLocked$.value)
276
275
  return;
277
276
  const button = event.currentTarget;
278
277
  popNewPropertyMenu(popupTargetFromElement(button), this.view, {
279
278
  title: this.wording(DATA_VIEW_PROPERTY_TYPE),
279
+ wording: this.wording,
280
280
  onAdd: id => {
281
281
  this.updateComplete
282
282
  .then(() => {
@@ -385,7 +385,7 @@ export const popPropertiesSetting = (target, props) => {
385
385
  class="properties-group-op"
386
386
  @click="${clickChangeAll}"
387
387
  >
388
- ${isAllShowed ? 'Hide All' : 'Show All'}
388
+ ${(props.wording ?? englishWording)(isAllShowed ? DATA_VIEW_HIDE_ALL : DATA_VIEW_SHOW_ALL)}
389
389
  </div>`;
390
390
  },
391
391
  },
@@ -2,7 +2,7 @@ import { type MenuConfig, type PopupTarget } from '../../../affine-components/co
2
2
  import { type Wording } from '../utils/wording.js';
3
3
  import type { Property } from '../view-manager/property.js';
4
4
  import type { SingleView } from '../view-manager/single-view.js';
5
- export declare const inputConfig: (property: Property, wording?: Wording) => (menu: import("../../../affine-components/context-menu/menu.js").Menu) => import("lit-html").TemplateResult<1> | undefined;
5
+ export declare const inputConfig: (property: Property, wording?: Wording) => MenuConfig;
6
6
  export declare const typeConfig: (property: Property, wording?: Wording) => (menu: import("../../../affine-components/context-menu/menu.js").Menu, index: number) => import("lit-html").TemplateResult | undefined;
7
7
  /** What a property menu's opener adds around the shared entries. */
8
8
  export type PropertyMenuSlots = {
@@ -32,7 +32,22 @@ export type PropertyMenuSlots = {
32
32
  * own copy of these entries; point it here in a dedicated pass.
33
33
  */
34
34
  export declare const propertyMenuItems: (property: Property, slots?: PropertyMenuSlots) => MenuConfig[];
35
- export declare const popPropertyMenu: (target: PopupTarget, property: Property, slots?: PropertyMenuSlots) => import("../../../affine-components/context-menu/menu-renderer.js").MenuHandler;
35
+ /**
36
+ * A property menu opened from a list of properties sits BESIDE the list, on
37
+ * the side with more room, and stays inside the viewport: below the row it
38
+ * covered the list, and in a host's narrow sidebar (labre, about 470px) it
39
+ * hid the very rows it edits, its « Type » sub-menu half off screen (labre
40
+ * recette of 1.1). The sub-menu then opens on the same side
41
+ * (`nestedSubMenuPosition`).
42
+ */
43
+ export declare const besideListMiddleware: () => {
44
+ name: string;
45
+ options?: any;
46
+ fn: (state: import("@floating-ui/dom").MiddlewareState) => import("@floating-ui/core").MiddlewareReturn | Promise<import("@floating-ui/core").MiddlewareReturn>;
47
+ }[];
48
+ export declare const popPropertyMenu: (target: PopupTarget, property: Property, slots?: PropertyMenuSlots, placement?: {
49
+ beside?: boolean;
50
+ }) => import("../../../affine-components/context-menu/menu-renderer.js").MenuHandler;
36
51
  /**
37
52
  * The type picker of a new property, as the virtual table header's "+" and
38
53
  * the record detail's "Add property" draw it: one entry per type the source
@@ -43,4 +58,5 @@ export declare const popPropertyMenu: (target: PopupTarget, property: Property,
43
58
  export declare const popNewPropertyMenu: (target: PopupTarget, view: SingleView, options: {
44
59
  title: string;
45
60
  onAdd: (id: string) => void;
61
+ wording?: Wording;
46
62
  }) => import("../../../affine-components/context-menu/menu-renderer.js").MenuHandler;
@@ -1,11 +1,45 @@
1
1
  import { menu, popMenu, } from '../../../affine-components/context-menu/index.js';
2
2
  import { TOOLBAR_DELETE, TOOLBAR_DUPLICATE, } from '../../../affine-shared/services/index.js';
3
3
  import { DeleteIcon, DuplicateIcon, ViewIcon } from '@blocksuite/icons/lit';
4
+ import { autoPlacement, offset, shift } from '@floating-ui/dom';
4
5
  import { html } from 'lit/static-html.js';
5
- import { DATA_VIEW_PROPERTY_HIDE_IN_VIEW, DATA_VIEW_PROPERTY_NAME, DATA_VIEW_PROPERTY_TYPE, DATA_VIEW_PROPERTY_TYPE_ENTRY, } from '../../translations.js';
6
+ import { DATA_VIEW_PROPERTIES_LOCKED, DATA_VIEW_PROPERTY_HIDE_IN_VIEW, DATA_VIEW_PROPERTY_NAME, DATA_VIEW_PROPERTY_TYPE, DATA_VIEW_PROPERTY_TYPE_ENTRY, } from '../../translations.js';
6
7
  import { renderUniLit } from '../utils/uni-component/index.js';
7
- import { englishWording } from '../utils/wording.js';
8
+ import { englishWording, propertyTypeName, } from '../utils/wording.js';
9
+ /** The name of the type `property` has, in the host language. */
10
+ const currentTypeName = (property, wording) => {
11
+ const meta = property.view.propertyMetas$.value.find(v => v.type === property.type$.value);
12
+ return meta ? propertyTypeName(wording, meta.config) : undefined;
13
+ };
14
+ /**
15
+ * The name of a property whose source its host locked (ADR 0035 amendment
16
+ * of 2026-10-29): shown, not editable, with the reason under it, so a menu
17
+ * that lost its rename, type, duplicate and delete says why.
18
+ */
19
+ const lockedNameConfig = (property, wording) => () => html `
20
+ <div
21
+ data-testid="dv-property-locked"
22
+ style="display:flex;flex-direction:column;gap:4px;padding:4px 6px;margin-bottom:8px;"
23
+ >
24
+ <div
25
+ style="display:flex;align-items:center;gap:4px;font-size:14px;line-height:22px;"
26
+ >
27
+ <div class="affine-database-column-type-menu-icon">
28
+ ${renderUniLit(property.icon)}
29
+ </div>
30
+ <span>${property.name$.value}</span>
31
+ </div>
32
+ <div
33
+ style="font-size:12px;line-height:20px;color:var(--affine-text-secondary-color);"
34
+ >
35
+ ${wording(DATA_VIEW_PROPERTIES_LOCKED)}
36
+ </div>
37
+ </div>
38
+ `;
8
39
  export const inputConfig = (property, wording = englishWording) => {
40
+ if (property.nameCanSet === false) {
41
+ return lockedNameConfig(property, wording);
42
+ }
9
43
  return menu.input({
10
44
  prefix: html `
11
45
  <div class="affine-database-column-type-menu-icon">
@@ -31,8 +65,7 @@ export const typeConfig = (property, wording = englishWording) => {
31
65
  class="affine-database-column-type-icon"
32
66
  style="color: var(--affine-text-secondary-color);gap:4px;font-size: 14px;"
33
67
  >
34
- ${renderUniLit(property.icon)}
35
- ${property.view.propertyMetas$.value.find(v => v.type === property.type$.value)?.config.name}
68
+ ${renderUniLit(property.icon)} ${currentTypeName(property, wording)}
36
69
  </div>`,
37
70
  options: {
38
71
  title: {
@@ -43,7 +76,7 @@ export const typeConfig = (property, wording = englishWording) => {
43
76
  items: property.view.propertyMetas$.value.map(config => {
44
77
  return menu.action({
45
78
  isSelected: config.type === property.type$.value,
46
- name: config.config.name,
79
+ name: propertyTypeName(wording, config.config),
47
80
  prefix: renderUniLit(config.renderer.icon),
48
81
  select: () => {
49
82
  if (property.type$.value === config.type) {
@@ -122,8 +155,22 @@ export const propertyMenuItems = (property, slots = {}) => {
122
155
  }),
123
156
  ];
124
157
  };
125
- export const popPropertyMenu = (target, property, slots) => popMenu(target, {
158
+ /**
159
+ * A property menu opened from a list of properties sits BESIDE the list, on
160
+ * the side with more room, and stays inside the viewport: below the row it
161
+ * covered the list, and in a host's narrow sidebar (labre, about 470px) it
162
+ * hid the very rows it edits, its « Type » sub-menu half off screen (labre
163
+ * recette of 1.1). The sub-menu then opens on the same side
164
+ * (`nestedSubMenuPosition`).
165
+ */
166
+ export const besideListMiddleware = () => [
167
+ autoPlacement({ allowedPlacements: ['right-start', 'left-start'] }),
168
+ offset(4),
169
+ shift({ padding: 8, crossAxis: true }),
170
+ ];
171
+ export const popPropertyMenu = (target, property, slots, placement = {}) => popMenu(target, {
126
172
  options: { items: propertyMenuItems(property, slots) },
173
+ middleware: placement.beside ? besideListMiddleware() : undefined,
127
174
  });
128
175
  /**
129
176
  * The type picker of a new property, as the virtual table header's "+" and
@@ -132,24 +179,28 @@ export const popPropertyMenu = (target, property, slots) => popMenu(target, {
132
179
  * source (every view), under the type's own name, and `onAdd` gets its id
133
180
  * so the opener can open its menu for the rename.
134
181
  */
135
- export const popNewPropertyMenu = (target, view, options) => popMenu(target, {
136
- options: {
137
- title: { text: options.title },
138
- items: [
139
- menu.group({
140
- items: view.propertyMetas$.value.map(meta => menu.action({
141
- name: meta.config.name,
142
- prefix: renderUniLit(meta.renderer.icon),
143
- select: () => {
144
- const id = view.propertyAdd('end', {
145
- type: meta.type,
146
- name: meta.config.name,
147
- });
148
- if (id)
149
- options.onAdd(id);
150
- },
151
- })),
152
- }),
153
- ],
154
- },
155
- });
182
+ export const popNewPropertyMenu = (target, view, options) => {
183
+ const wording = options.wording ?? englishWording;
184
+ return popMenu(target, {
185
+ options: {
186
+ title: { text: options.title },
187
+ items: [
188
+ menu.group({
189
+ items: view.propertyMetas$.value.map(meta => menu.action({
190
+ name: propertyTypeName(wording, meta.config),
191
+ prefix: renderUniLit(meta.renderer.icon),
192
+ select: () => {
193
+ // Written resolved: a stored name is never re-translated.
194
+ const id = view.propertyAdd('end', {
195
+ type: meta.type,
196
+ name: propertyTypeName(wording, meta.config),
197
+ });
198
+ if (id)
199
+ options.onAdd(id);
200
+ },
201
+ })),
202
+ }),
203
+ ],
204
+ },
205
+ });
206
+ };
@@ -11,6 +11,14 @@ import type { ViewManager } from '../view-manager/view-manager.js';
11
11
  import { type CellStatus, type DataSourceHydration, type ValuesState } from './hydration.js';
12
12
  export interface DataSource extends DataSourceHydration {
13
13
  readonly$: ReadonlySignal<boolean>;
14
+ /**
15
+ * The current view's source refuses new, renamed, deleted, duplicated or
16
+ * retyped properties (a host's policy, ADR 0035 amendment of 2026-10-29).
17
+ * Absent, nothing is locked. Read through `SingleView.propertiesLocked$`.
18
+ */
19
+ propertiesLocked$?: ReadonlySignal<boolean>;
20
+ /** The current view's source refuses new rows; absent, it takes them. */
21
+ rowsLocked$?: ReadonlySignal<boolean>;
14
22
  properties$: ReadonlySignal<string[]>;
15
23
  featureFlags$: ReadonlySignal<DatabaseFlags>;
16
24
  cellValueGet(rowId: string, propertyId: string): unknown;
@@ -69,6 +77,9 @@ export declare abstract class DataSourceBase implements DataSource {
69
77
  /** Every cell is `ready` unless a source says otherwise (ADR 0035 §9e). */
70
78
  cellStatus(_rowId: string, _propertyId: string): CellStatus;
71
79
  valuesState$: ReadonlySignal<ValuesState>;
80
+ /** Nothing is locked unless a source says so (`HostedDataSourceBase`). */
81
+ readonly propertiesLocked$: ReadonlySignal<boolean>;
82
+ readonly rowsLocked$: ReadonlySignal<boolean>;
72
83
  propertyTypeCanSet(propertyId: string): boolean;
73
84
  propertyCanDuplicate(propertyId: string): boolean;
74
85
  propertyCanDelete(propertyId: string): boolean;