@formicoidea/labre-core 1.0.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 (207) hide show
  1. package/architecture/code.c4 +48 -44
  2. package/architecture/likec4.config.json +3 -3
  3. package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +1 -1
  4. package/dist/_pkgs/affine-block-root/configs/toolbar.js +4 -2
  5. package/dist/_pkgs/affine-block-surface/extensions/reading-toolbar.js +12 -12
  6. package/dist/_pkgs/affine-components/context-menu/menu-all.d.ts +1 -0
  7. package/dist/_pkgs/affine-components/context-menu/menu-renderer.d.ts +3 -1
  8. package/dist/_pkgs/affine-components/context-menu/menu-renderer.js +9 -1
  9. package/dist/_pkgs/affine-components/context-menu/menu.d.ts +7 -0
  10. package/dist/_pkgs/affine-components/context-menu/menu.js +5 -0
  11. package/dist/_pkgs/affine-components/context-menu/sub-menu.d.ts +19 -1
  12. package/dist/_pkgs/affine-components/context-menu/sub-menu.js +46 -7
  13. package/dist/_pkgs/affine-components/highlight-dropdown-menu/highlight-duotone-icon.js +8 -8
  14. package/dist/_pkgs/affine-components/highlight-dropdown-menu/text-duotone-icon.js +11 -11
  15. package/dist/_pkgs/affine-database-block/configs/slash-menu.d.ts +24 -0
  16. package/dist/_pkgs/affine-database-block/configs/slash-menu.js +52 -37
  17. package/dist/_pkgs/affine-database-block/creation-menu.d.ts +6 -0
  18. package/dist/_pkgs/affine-database-block/data-source.d.ts +6 -0
  19. package/dist/_pkgs/affine-database-block/data-source.js +16 -0
  20. package/dist/_pkgs/affine-database-block/database-block-styles.js +4 -3
  21. package/dist/_pkgs/affine-database-block/database-block.d.ts +23 -0
  22. package/dist/_pkgs/affine-database-block/database-block.js +129 -19
  23. package/dist/_pkgs/affine-database-block/database-dnd-preview-block.js +5 -1
  24. package/dist/_pkgs/affine-database-block/detail-panel/block-renderer.js +5 -2
  25. package/dist/_pkgs/affine-database-block/detail-panel/note-renderer.js +3 -2
  26. package/dist/_pkgs/affine-database-block/external-data-source.d.ts +17 -0
  27. package/dist/_pkgs/affine-database-block/external-data-source.js +54 -4
  28. package/dist/_pkgs/affine-database-block/index.d.ts +3 -0
  29. package/dist/_pkgs/affine-database-block/index.js +5 -0
  30. package/dist/_pkgs/affine-database-block/linked-view.d.ts +59 -0
  31. package/dist/_pkgs/affine-database-block/linked-view.js +131 -1
  32. package/dist/_pkgs/affine-database-block/properties/created-time/define.js +3 -1
  33. package/dist/_pkgs/affine-database-block/properties/link/cell-renderer.js +3 -2
  34. package/dist/_pkgs/affine-database-block/properties/link/define.js +3 -1
  35. package/dist/_pkgs/affine-database-block/properties/rich-text/define.js +3 -1
  36. package/dist/_pkgs/affine-database-block/properties/text/hosted.js +1 -0
  37. package/dist/_pkgs/affine-database-block/properties/title/define.js +3 -1
  38. package/dist/_pkgs/affine-database-block/properties/title/hosted.js +1 -0
  39. package/dist/_pkgs/affine-database-block/service/new-row-menu.d.ts +1 -1
  40. package/dist/_pkgs/affine-database-block/service/new-row-menu.js +1 -1
  41. package/dist/_pkgs/affine-database-block/translations.d.ts +29 -0
  42. package/dist/_pkgs/affine-database-block/translations.js +104 -0
  43. package/dist/_pkgs/affine-database-block/turn-into-source.d.ts +38 -0
  44. package/dist/_pkgs/affine-database-block/turn-into-source.js +187 -0
  45. package/dist/_pkgs/affine-database-block/view-deletion.d.ts +15 -0
  46. package/dist/_pkgs/affine-database-block/view-deletion.js +113 -0
  47. package/dist/_pkgs/affine-database-block/views/index.d.ts +7 -9
  48. package/dist/_pkgs/affine-database-block/views/index.js +7 -10
  49. package/dist/_pkgs/affine-database-engine/core/common/properties.js +7 -7
  50. package/dist/_pkgs/affine-database-engine/core/common/property-menu.d.ts +18 -2
  51. package/dist/_pkgs/affine-database-engine/core/common/property-menu.js +78 -27
  52. package/dist/_pkgs/affine-database-engine/core/data-source/base.d.ts +11 -0
  53. package/dist/_pkgs/affine-database-engine/core/data-source/base.js +3 -0
  54. package/dist/_pkgs/affine-database-engine/core/detail/detail.js +8 -5
  55. package/dist/_pkgs/affine-database-engine/core/detail/field.js +14 -7
  56. package/dist/_pkgs/affine-database-engine/core/filter/add-filter.js +4 -2
  57. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/boolean.js +7 -4
  58. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/create.d.ts +9 -1
  59. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/create.js +2 -0
  60. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/date.js +7 -4
  61. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/multi-tag.js +17 -8
  62. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/number.js +6 -6
  63. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/string.js +19 -12
  64. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/tag.js +9 -4
  65. package/dist/_pkgs/affine-database-engine/core/filter/filter-fn/unknown.js +7 -4
  66. package/dist/_pkgs/affine-database-engine/core/filter/literal/define.js +6 -4
  67. package/dist/_pkgs/affine-database-engine/core/filter/literal/matcher.d.ts +2 -1
  68. package/dist/_pkgs/affine-database-engine/core/filter/literal/matcher.js +3 -2
  69. package/dist/_pkgs/affine-database-engine/core/filter/literal/types.d.ts +4 -1
  70. package/dist/_pkgs/affine-database-engine/core/filter/literal/types.js +1 -1
  71. package/dist/_pkgs/affine-database-engine/core/group-by/define.js +11 -9
  72. package/dist/_pkgs/affine-database-engine/core/group-by/group-title.js +16 -6
  73. package/dist/_pkgs/affine-database-engine/core/group-by/locate-row.d.ts +16 -0
  74. package/dist/_pkgs/affine-database-engine/core/group-by/locate-row.js +32 -0
  75. package/dist/_pkgs/affine-database-engine/core/group-by/renderer/date-group.js +10 -3
  76. package/dist/_pkgs/affine-database-engine/core/group-by/setting.d.ts +2 -0
  77. package/dist/_pkgs/affine-database-engine/core/group-by/setting.js +40 -28
  78. package/dist/_pkgs/affine-database-engine/core/group-by/trait.d.ts +3 -0
  79. package/dist/_pkgs/affine-database-engine/core/group-by/trait.js +8 -0
  80. package/dist/_pkgs/affine-database-engine/core/group-by/types.d.ts +3 -1
  81. package/dist/_pkgs/affine-database-engine/core/group-by/types.js +1 -1
  82. package/dist/_pkgs/affine-database-engine/core/property/types.d.ts +7 -0
  83. package/dist/_pkgs/affine-database-engine/core/property/types.js +1 -1
  84. package/dist/_pkgs/affine-database-engine/core/sort/add-sort.js +3 -1
  85. package/dist/_pkgs/affine-database-engine/core/statistics/any.js +43 -21
  86. package/dist/_pkgs/affine-database-engine/core/statistics/checkbox.js +29 -14
  87. package/dist/_pkgs/affine-database-engine/core/statistics/number.js +54 -31
  88. package/dist/_pkgs/affine-database-engine/core/statistics/types.d.ts +8 -0
  89. package/dist/_pkgs/affine-database-engine/core/statistics/types.js +2 -1
  90. package/dist/_pkgs/affine-database-engine/core/utils/wording.d.ts +20 -1
  91. package/dist/_pkgs/affine-database-engine/core/utils/wording.js +24 -0
  92. package/dist/_pkgs/affine-database-engine/core/view-manager/property.d.ts +7 -0
  93. package/dist/_pkgs/affine-database-engine/core/view-manager/property.js +29 -5
  94. package/dist/_pkgs/affine-database-engine/core/view-manager/single-view.d.ts +14 -0
  95. package/dist/_pkgs/affine-database-engine/core/view-manager/single-view.js +11 -0
  96. package/dist/_pkgs/affine-database-engine/property-presets/checkbox/define.js +3 -1
  97. package/dist/_pkgs/affine-database-engine/property-presets/date/define.js +3 -1
  98. package/dist/_pkgs/affine-database-engine/property-presets/multi-select/define.js +3 -1
  99. package/dist/_pkgs/affine-database-engine/property-presets/number/define.js +3 -1
  100. package/dist/_pkgs/affine-database-engine/property-presets/number/utils/formats.d.ts +3 -0
  101. package/dist/_pkgs/affine-database-engine/property-presets/number/utils/formats.js +56 -9
  102. package/dist/_pkgs/affine-database-engine/property-presets/progress/define.js +3 -1
  103. package/dist/_pkgs/affine-database-engine/property-presets/select/define.js +3 -1
  104. package/dist/_pkgs/affine-database-engine/property-presets/text/define.js +3 -1
  105. package/dist/_pkgs/affine-database-engine/translations.d.ts +133 -5
  106. package/dist/_pkgs/affine-database-engine/translations.js +625 -9
  107. package/dist/_pkgs/affine-database-engine/view-presets/convert.js +0 -28
  108. package/dist/_pkgs/affine-database-engine/view-presets/effect.js +0 -2
  109. package/dist/_pkgs/affine-database-engine/view-presets/flat/flat-converts.js +2 -4
  110. package/dist/_pkgs/affine-database-engine/view-presets/flat/flat-view-ui.d.ts +57 -0
  111. package/dist/_pkgs/affine-database-engine/view-presets/flat/flat-view-ui.js +113 -4
  112. package/dist/_pkgs/affine-database-engine/view-presets/gallery/gallery-view.js +2 -1
  113. package/dist/_pkgs/affine-database-engine/view-presets/index.d.ts +0 -2
  114. package/dist/_pkgs/affine-database-engine/view-presets/index.js +0 -3
  115. package/dist/_pkgs/affine-database-engine/view-presets/kanban/kanban-view-manager.js +2 -0
  116. package/dist/_pkgs/affine-database-engine/view-presets/kanban/mobile/group.js +7 -5
  117. package/dist/_pkgs/affine-database-engine/view-presets/kanban/mobile/kanban-view-ui-logic.d.ts +5 -0
  118. package/dist/_pkgs/affine-database-engine/view-presets/kanban/mobile/kanban-view-ui-logic.js +25 -3
  119. package/dist/_pkgs/affine-database-engine/view-presets/kanban/mobile/menu.js +12 -6
  120. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/controller/selection.d.ts +8 -0
  121. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/controller/selection.js +35 -12
  122. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/group.js +7 -5
  123. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/header.js +5 -1
  124. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/kanban-view-ui-logic.d.ts +5 -0
  125. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/kanban-view-ui-logic.js +8 -4
  126. package/dist/_pkgs/affine-database-engine/view-presets/kanban/pc/menu.js +12 -6
  127. package/dist/_pkgs/affine-database-engine/view-presets/list/list-view.js +2 -1
  128. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/column-header.js +6 -4
  129. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/group.js +6 -5
  130. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/header.js +2 -2
  131. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/table-view-ui-logic.d.ts +4 -3
  132. package/dist/_pkgs/affine-database-engine/view-presets/table/mobile/table-view-ui-logic.js +29 -5
  133. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/controller/clipboard.js +6 -2
  134. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/controller/selection.js +2 -0
  135. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/group.js +6 -5
  136. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/header/column-header.js +11 -7
  137. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/header/database-header-column.js +16 -12
  138. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/header/number-format-bar.js +4 -2
  139. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/menu.js +9 -6
  140. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/table-view-ui-logic.d.ts +8 -1
  141. package/dist/_pkgs/affine-database-engine/view-presets/table/pc/table-view-ui-logic.js +29 -7
  142. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/controller/clipboard.js +6 -2
  143. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/controller/selection.js +2 -0
  144. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/bottom/group-footer.js +5 -2
  145. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/bottom/stats/column-stats-column.d.ts +1 -1
  146. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/bottom/stats/column-stats-column.js +14 -6
  147. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/group-header.js +5 -2
  148. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/group-title.js +5 -3
  149. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/header/column-header.js +17 -10
  150. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/header/number-format-bar.js +4 -2
  151. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/group/top/header/single-column-header.js +20 -14
  152. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/row/menu.js +9 -6
  153. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/table-view-ui-logic.d.ts +6 -1
  154. package/dist/_pkgs/affine-database-engine/view-presets/table/pc-virtual/table-view-ui-logic.js +30 -3
  155. package/dist/_pkgs/affine-database-engine/view-presets/table/stats/column-stats-column.d.ts +1 -1
  156. package/dist/_pkgs/affine-database-engine/view-presets/table/stats/column-stats-column.js +14 -6
  157. package/dist/_pkgs/affine-database-engine/view-presets/table/table-view-manager.js +2 -0
  158. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/filter/condition-view.d.ts +2 -0
  159. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/filter/condition-view.js +13 -6
  160. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/filter/group-panel-view.js +20 -13
  161. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/filter/root-panel-view.js +10 -5
  162. package/dist/_pkgs/affine-database-engine/widget-presets/quick-setting-bar/sort/root-panel.js +8 -5
  163. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/search/search.js +2 -2
  164. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/table-add-row/add-row.d.ts +6 -0
  165. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/table-add-row/add-row.js +49 -4
  166. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/view-options/view-settings.d.ts +3 -3
  167. package/dist/_pkgs/affine-database-engine/widget-presets/tools/presets/view-options/view-settings.js +3 -3
  168. package/dist/_pkgs/affine-database-engine/widget-presets/views-bar/views-view.js +5 -0
  169. package/dist/_pkgs/affine-database-view-block/configs/slash-menu.js +6 -3
  170. package/dist/_pkgs/affine-database-view-block/data-view-block.js +5 -4
  171. package/dist/_pkgs/affine-fragment-doc-title/doc-title.d.ts +3 -2
  172. package/dist/_pkgs/affine-fragment-doc-title/doc-title.js +3 -2
  173. package/dist/_pkgs/affine-shared/services/data-source-picker-service.d.ts +130 -1
  174. package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/index.js +26 -26
  175. package/dist/_pkgs/affine-widget-keyboard-toolbar/config.js +10 -21
  176. package/dist/_pkgs/affine-widget-keyboard-toolbar/icons.js +98 -98
  177. package/dist/_pkgs/affine-widget-page-dragging-area/index.js +10 -10
  178. package/dist/_pkgs/affine-widget-viewport-overlay/index.js +13 -13
  179. package/package.json +1 -1
  180. package/dist/_pkgs/affine-database-engine/view-presets/calendar/calendar-view-manager.d.ts +0 -70
  181. package/dist/_pkgs/affine-database-engine/view-presets/calendar/calendar-view-manager.js +0 -449
  182. package/dist/_pkgs/affine-database-engine/view-presets/calendar/define.d.ts +0 -10
  183. package/dist/_pkgs/affine-database-engine/view-presets/calendar/define.js +0 -29
  184. package/dist/_pkgs/affine-database-engine/view-presets/calendar/effect.d.ts +0 -1
  185. package/dist/_pkgs/affine-database-engine/view-presets/calendar/effect.js +0 -4
  186. package/dist/_pkgs/affine-database-engine/view-presets/calendar/index.d.ts +0 -6
  187. package/dist/_pkgs/affine-database-engine/view-presets/calendar/index.js +0 -6
  188. package/dist/_pkgs/affine-database-engine/view-presets/calendar/layout.d.ts +0 -37
  189. package/dist/_pkgs/affine-database-engine/view-presets/calendar/layout.js +0 -138
  190. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/actions.d.ts +0 -7
  191. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/actions.js +0 -64
  192. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/dnd.d.ts +0 -32
  193. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/dnd.js +0 -159
  194. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/effect.d.ts +0 -1
  195. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/effect.js +0 -7
  196. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/hit-test.d.ts +0 -1
  197. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/hit-test.js +0 -28
  198. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/styles.d.ts +0 -1
  199. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/styles.js +0 -707
  200. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/view.d.ts +0 -89
  201. package/dist/_pkgs/affine-database-engine/view-presets/calendar/pc/view.js +0 -849
  202. package/dist/_pkgs/affine-database-engine/view-presets/calendar/renderer.d.ts +0 -2
  203. package/dist/_pkgs/affine-database-engine/view-presets/calendar/renderer.js +0 -8
  204. package/dist/_pkgs/affine-database-engine/view-presets/calendar/source.d.ts +0 -8
  205. package/dist/_pkgs/affine-database-engine/view-presets/calendar/source.js +0 -3
  206. package/dist/_pkgs/affine-database-engine/view-presets/calendar/types.d.ts +0 -79
  207. package/dist/_pkgs/affine-database-engine/view-presets/calendar/types.js +0 -1
@@ -7,12 +7,14 @@ import { insertDatabaseBlockCommand } from '../commands.js';
7
7
  import { DatabaseCreationMenuIdentifier } from '../creation-menu.js';
8
8
  import { createHostedDatabaseView } from '../linked-view.js';
9
9
  import { KanbanViewTooltip, TableViewTooltip } from './tooltips.js';
10
+ import { DATABASE_SLASH_KANBAN_DESCRIPTION, DATABASE_SLASH_TABLE_DESCRIPTION, } from '../translations.js';
11
+ import { VIEW_NAME_TABLE, VIEW_NAME_KANBAN, } from '../../affine-database-engine/translations.js';
10
12
  /**
11
13
  * With a data-source picker the host owns database creation (ADR 0035 §5):
12
14
  * its `createDatabase` is the only way in, and without it no entry creates
13
15
  * one. Without a picker, a native inline database, as in 0.48.
14
16
  */
15
- function canCreateDatabase(std) {
17
+ export function canCreateDatabase(std) {
16
18
  const picker = std.getOptional(DataSourcePickerProvider);
17
19
  return !picker || !!picker.createDatabase;
18
20
  }
@@ -55,38 +57,45 @@ function insertNativeDatabase(std, viewType, initialRows) {
55
57
  * - a picker alone (a core-only host): the host's `createDatabase` first,
56
58
  * then a view of its first source, as in 0.61;
57
59
  * - no picker: a native inline database with 3 rows, as in 0.48.
60
+ *
61
+ * The one creation gesture: the slash entries and the mobile keyboard
62
+ * toolbar's "Table view" / "Kanban view" both run it (ADR 0035 amendment of
63
+ * 2026-10-29), so neither inserts native rows a host's picker would not see.
58
64
  */
59
- function createDatabaseAction(viewType) {
60
- return ({ std }) => {
61
- const picker = std.getOptional(DataSourcePickerProvider);
62
- if (!picker) {
63
- insertNativeDatabase(std, viewType);
64
- return;
65
- }
66
- const menu = std.getOptional(DatabaseCreationMenuIdentifier);
67
- if (!menu) {
68
- const [, { selectedModels }] = std.command.exec(getSelectedModelsCommand);
69
- const after = selectedModels?.at(-1);
70
- if (!after)
71
- return;
72
- createHostedDatabaseView(std, picker, {
73
- after,
74
- viewType,
75
- removeEmptyLine: true,
76
- }).catch(console.error);
77
- return;
78
- }
79
- const blockId = insertNativeDatabase(std, viewType, 0);
80
- if (!blockId)
65
+ export function createDatabaseByGesture(std, viewType) {
66
+ if (!canCreateDatabase(std))
67
+ return;
68
+ const picker = std.getOptional(DataSourcePickerProvider);
69
+ if (!picker) {
70
+ insertNativeDatabase(std, viewType);
71
+ return;
72
+ }
73
+ const menu = std.getOptional(DatabaseCreationMenuIdentifier);
74
+ if (!menu) {
75
+ const [, { selectedModels }] = std.command.exec(getSelectedModelsCommand);
76
+ const after = selectedModels?.at(-1);
77
+ if (!after)
81
78
  return;
82
- std.host.updateComplete
83
- .then(() => {
84
- const anchor = std.view.getBlock(blockId);
85
- if (anchor)
86
- menu.open({ std, blockId, anchor });
87
- })
88
- .catch(console.error);
89
- };
79
+ createHostedDatabaseView(std, picker, {
80
+ after,
81
+ viewType,
82
+ removeEmptyLine: true,
83
+ }).catch(console.error);
84
+ return;
85
+ }
86
+ const blockId = insertNativeDatabase(std, viewType, 0);
87
+ if (!blockId)
88
+ return;
89
+ std.host.updateComplete
90
+ .then(() => {
91
+ const anchor = std.view.getBlock(blockId);
92
+ if (anchor)
93
+ menu.open({ std, blockId, anchor });
94
+ })
95
+ .catch(console.error);
96
+ }
97
+ function createDatabaseAction(viewType) {
98
+ return ({ std }) => createDatabaseByGesture(std, viewType);
90
99
  }
91
100
  /**
92
101
  * The slash entries that CREATE a database, registered by the gated
@@ -97,13 +106,16 @@ export const databaseSlashMenuConfig = {
97
106
  disableWhen: ({ model }) => model.flavour === 'affine:database',
98
107
  items: [
99
108
  {
100
- name: 'Table View',
101
- description: 'Display items in a table format.',
109
+ name: VIEW_NAME_TABLE[1],
110
+ nameWording: VIEW_NAME_TABLE,
111
+ description: DATABASE_SLASH_TABLE_DESCRIPTION[1],
112
+ descriptionWording: DATABASE_SLASH_TABLE_DESCRIPTION,
102
113
  searchAlias: ['database'],
103
114
  icon: DatabaseTableViewIcon(),
104
115
  tooltip: {
105
116
  figure: TableViewTooltip,
106
- caption: 'Table View',
117
+ caption: VIEW_NAME_TABLE[1],
118
+ captionWording: VIEW_NAME_TABLE,
107
119
  },
108
120
  group: '7_Database@0',
109
121
  when: ({ model, std }) => !isInsideBlockByFlavour(model.store, model, 'affine:edgeless-text') &&
@@ -111,13 +123,16 @@ export const databaseSlashMenuConfig = {
111
123
  action: createDatabaseAction(viewPresets.tableViewMeta.type),
112
124
  },
113
125
  {
114
- name: 'Kanban View',
115
- description: 'Visualize data in a dashboard.',
126
+ name: VIEW_NAME_KANBAN[1],
127
+ nameWording: VIEW_NAME_KANBAN,
128
+ description: DATABASE_SLASH_KANBAN_DESCRIPTION[1],
129
+ descriptionWording: DATABASE_SLASH_KANBAN_DESCRIPTION,
116
130
  searchAlias: ['database'],
117
131
  icon: DatabaseKanbanViewIcon(),
118
132
  tooltip: {
119
133
  figure: KanbanViewTooltip,
120
- caption: 'Kanban View',
134
+ caption: VIEW_NAME_KANBAN[1],
135
+ captionWording: VIEW_NAME_KANBAN,
121
136
  },
122
137
  group: '7_Database@2',
123
138
  when: ({ model, std }) => !isInsideBlockByFlavour(model.store, model, 'affine:edgeless-text') &&
@@ -7,6 +7,12 @@ export interface DatabaseCreationRequest {
7
7
  blockId: string;
8
8
  /** The block's element, to hang the menu from. */
9
9
  anchor: HTMLElement;
10
+ /**
11
+ * The view to connect, when the block already has a source the host no
12
+ * longer resolves ("Connect a source" of its empty view, ADR 0035
13
+ * amendment of 2026-10-29). Absent: the block's first view.
14
+ */
15
+ viewId?: string;
10
16
  }
11
17
  /**
12
18
  * What a database creation entry opens over the block it just inserted
@@ -86,5 +86,11 @@ export declare class DatabaseBlockDataSource extends HostedDataSourceBase {
86
86
  * to be chosen (the creation popover, ADR 0035 amendment of 2026-10-28).
87
87
  */
88
88
  export declare const databaseViewInitTemplate: (datasource: DatabaseBlockDataSource, viewType: string, std?: BlockStdScope, rows?: number) => void;
89
+ /**
90
+ * "Create Table": the selected blocks become a database's rows. Under a host
91
+ * that requires a source they become a source of the host's instead
92
+ * (`turnBlocksIntoSource`, ADR 0035 amendment of 2026-10-29), never a
93
+ * native database it would show read-only.
94
+ */
89
95
  export declare const convertToDatabase: (host: EditorHost, viewType: string) => void;
90
96
  export {};
@@ -7,6 +7,7 @@ import { computed, signal } from '@preact/signals-core';
7
7
  import { getIcon } from './block-icons.js';
8
8
  import { HostedDataSourceBase } from './external-data-source.js';
9
9
  import { databaseBlockProperties, databasePropertyConverts, } from './properties/index.js';
10
+ import { canTurnIntoSource, turnBlocksIntoSource } from './turn-into-source.js';
10
11
  import { addProperty, copyCellsByProperty, deleteRows, getCell, getProperty, updateCell, updateCells, updateProperty, } from './utils/block-utils.js';
11
12
  /**
12
13
  * The inline blob source: rows are the block's children, cells/columns live in
@@ -405,6 +406,12 @@ export const databaseViewInitTemplate = (datasource, viewType, std, rows = 3) =>
405
406
  });
406
407
  datasource.viewManager.viewAdd(viewType, viewDefaultName(datasource.viewMetaGet(viewType), std));
407
408
  };
409
+ /**
410
+ * "Create Table": the selected blocks become a database's rows. Under a host
411
+ * that requires a source they become a source of the host's instead
412
+ * (`turnBlocksIntoSource`, ADR 0035 amendment of 2026-10-29), never a
413
+ * native database it would show read-only.
414
+ */
408
415
  export const convertToDatabase = (host, viewType) => {
409
416
  const [_, ctx] = host.std.command.exec(getSelectedModelsCommand, {
410
417
  types: ['block', 'text'],
@@ -413,6 +420,15 @@ export const convertToDatabase = (host, viewType) => {
413
420
  const firstModel = selectedModels?.[0];
414
421
  if (!firstModel)
415
422
  return;
423
+ if (canTurnIntoSource(host.std)) {
424
+ turnBlocksIntoSource(host.std, selectedModels, viewType)
425
+ .then(written => {
426
+ if (written)
427
+ host.selection.clear();
428
+ })
429
+ .catch(console.error);
430
+ return;
431
+ }
416
432
  host.store.captureSync();
417
433
  const parentModel = host.store.getParent(firstModel);
418
434
  if (!parentModel) {
@@ -50,11 +50,12 @@ export const databaseHeaderContainerStyles = css({
50
50
  display: 'flex',
51
51
  flexDirection: 'column',
52
52
  // Drawn as the page, which scrolls sideways under a wide table: the view
53
- // bar stays in view, a viewport wide.
53
+ // bar stays in view, a viewport wide, inside the inset the full page's
54
+ // root sets to keep it under the centred heading (0 elsewhere).
54
55
  'affine-database[data-standalone] &': {
55
56
  position: 'sticky',
56
- left: 'var(--affine-editor-side-padding, 24px)',
57
- width: 'calc(100cqw - 2 * var(--affine-editor-side-padding, 24px))',
57
+ left: 'calc(var(--affine-editor-side-padding, 24px) + var(--affine-database-page-inset, 0px))',
58
+ width: 'calc(100cqw - 2 * var(--affine-editor-side-padding, 24px) - 2 * var(--affine-database-page-inset, 0px))',
58
59
  },
59
60
  });
60
61
  export const databaseTitleRowStyles = css({
@@ -60,6 +60,29 @@ export declare class DatabaseBlockComponent extends CaptionedBlockComponent<Data
60
60
  get isCommentHighlighted(): boolean;
61
61
  get topContenteditableElement(): BlockComponent<import("../store/index.js").BlockModel<object>, import("../std/index.js").BlockService, string> | null;
62
62
  private readonly renderSourceMissing;
63
+ /**
64
+ * A database a host that requires a source must connect first
65
+ * (`sourceRequirement` is `'connect'`, ADR 0035 amendment of 2026-10-29):
66
+ * no source and no row, or a hosted source the host no longer resolves.
67
+ * Drawn in place of the tools and the body: no row, no "New", no cell to
68
+ * type or paste into, so nothing is written. A hosted block keeps its view
69
+ * bar, since another of its views may still read a source. The button
70
+ * reopens the creation popover over the block, for the current view; a
71
+ * read-only document, or a host without the bundle's creation menu, gets
72
+ * the notice alone. Takes precedence over "Source not found" (§7).
73
+ */
74
+ private readonly renderSourceRequired;
75
+ /** "Move into a database" is waiting for the host: one call at a time. */
76
+ private readonly moving$;
77
+ private readonly moveIntoSource;
78
+ /**
79
+ * Native rows under a host that requires a source (`sourceRequirement` is
80
+ * `'read-only'`): the rows render, locked (`locked$`), with this notice and,
81
+ * where the host turns content into a source (`createDatabaseFrom`) and
82
+ * the document is writable, "Move into a database"; a spinner while the
83
+ * host answers.
84
+ */
85
+ private renderReadOnlyNotice;
63
86
  private renderDatabaseOps;
64
87
  connectedCallback(): void;
65
88
  disconnectedCallback(): void;
@@ -2,13 +2,13 @@ import { CaptionedBlockComponent } from '../affine-components/caption/index.js';
2
2
  import { menu, popMenu, popupTargetFromElement, } from '../affine-components/context-menu/index.js';
3
3
  import { DropIndicator } from '../affine-components/drop-indicator/index.js';
4
4
  import { PeekViewProvider } from '../affine-components/peek/index.js';
5
+ import { LoadingIcon } from '../affine-components/icons/index.js';
5
6
  import { toast } from '../affine-components/toast/index.js';
6
7
  import { EDGELESS_TOP_CONTENTEDITABLE_SELECTOR } from '../affine-shared/consts/index.js';
7
8
  import { getScrollContainer } from '../affine-shared/utils/index.js';
8
- import { BlockCommentManager, CommentProviderIdentifier, DocModeProvider, NotificationProvider, PropertiesPanelProvider, TelemetryProvider, TOAST_COPIED_TO_CLIPBOARD, translateKey, } from '../affine-shared/services/index.js';
9
+ import { BlockCommentManager, CommentProviderIdentifier, DataSourcePickerProvider, DocModeProvider, NotificationProvider, PropertiesPanelProvider, TelemetryProvider, TOAST_COPIED_TO_CLIPBOARD, translateKey, TOOLBAR_COPY, TOOLBAR_COMMENT, } from '../affine-shared/services/index.js';
9
10
  import { getDropResult } from '../affine-widget-drag-handle/index.js';
10
11
  import { createRecordDetail, createUniComponentFromWebComponent, DataViewRootUILogic, DataViewEventTraceKey, defineUniComponent, ExternalGroupByConfigProvider, NewRowMenuOpenerKey, PageLayoutKey, PropertiesPanelOpenerKey, renderUniLit, traceViewEvent, uniMap, ViewCreationOffKey, } from '../affine-database-engine/index.js';
11
- import { CalendarExternalSourceProvider } from '../affine-database-engine/view-presets/index.js';
12
12
  import { widgetPresets } from '../affine-database-engine/widget-presets/index.js';
13
13
  import { Rect } from '../global/gfx/index.js';
14
14
  import { CommentIcon, CopyIcon, DeleteIcon, MoreHorizontalIcon, } from '@blocksuite/icons/lit';
@@ -21,16 +21,19 @@ import { html, nothing } from 'lit';
21
21
  import { keyed } from 'lit/directives/keyed.js';
22
22
  import { popSideDetail } from './components/layout.js';
23
23
  import { DatabaseConfigExtension } from './config.js';
24
+ import { DatabaseCreationMenuIdentifier } from './creation-menu.js';
24
25
  import { EditorHostKey } from './context/host-context.js';
25
26
  import { DatabaseBlockDataSource } from './data-source.js';
26
27
  import { HostedDataSourceBase } from './external-data-source.js';
27
28
  import { databaseBlockStyles, databaseContentStyles, databaseHeaderBarStyles, databaseHeaderContainerStyles, databaseOpsStyles, databaseSourceMissingHintStyles, databaseSourceMissingStyles, databaseTitleRowStyles, databaseTitleStyles, databaseToolbarRowStyles, databaseViewBarContainerStyles, } from './database-block-styles.js';
28
29
  import { BlockRenderer } from './detail-panel/block-renderer.js';
29
30
  import { NoteRenderer } from './detail-panel/note-renderer.js';
31
+ import { sourceRequirement } from './linked-view.js';
30
32
  import { DatabaseSelection } from './selection.js';
31
33
  import { isStandaloneDatabase } from './standalone-layout.js';
32
34
  import { databaseOpenRowRequest, DatabaseDataSourceProvider, DatabaseInitialViewProvider, DatabaseNewRowMenuProvider, DatabaseOpenRowProvider, openDatabasePropertiesPanel, rowOpenedParams, } from './service/index.js';
33
- import { DATABASE_NEW_RECORD_MORE, DATABASE_SOURCE_MISSING, DATABASE_SOURCE_MISSING_HINT, } from './translations.js';
35
+ import { DATABASE_CREATION_TITLE, DATABASE_MOVE_INTO_SOURCE, DATABASE_NEW_RECORD_MORE, DATABASE_SOURCE_MISSING, DATABASE_SOURCE_MISSING_HINT, DATABASE_SOURCE_REQUIRED, DATABASE_SOURCE_REQUIRED_HINT, DATABASE_SOURCE_REQUIRED_READ_ONLY, DATABASE_DELETE, DATABASE_TITLE_LABEL, } from './translations.js';
36
+ import { canTurnIntoSource, moveDatabaseRowsIntoSource, } from './turn-into-source.js';
34
37
  import { currentViewStorage, initialViewId } from './utils/current-view.js';
35
38
  import { getSingleDocIdFromText } from './utils/title-doc.js';
36
39
  import { DatabaseViewCreationIdentifier } from './view-creation.js';
@@ -42,14 +45,14 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
42
45
  items: [
43
46
  menu.input({
44
47
  initialValue: this.model.props.title.toString(),
45
- placeholder: 'Database title',
48
+ placeholder: translateKey(this.std, ...DATABASE_TITLE_LABEL),
46
49
  onChange: text => {
47
50
  this.model.props.title.replace(0, this.model.props.title.length, text);
48
51
  },
49
52
  }),
50
53
  menu.action({
51
54
  prefix: CommentIcon(),
52
- name: 'Comment',
55
+ name: translateKey(this.std, ...TOOLBAR_COMMENT),
53
56
  hide: () => !this.std.getOptional(CommentProviderIdentifier),
54
57
  select: () => {
55
58
  this.std.getOptional(CommentProviderIdentifier)?.addComment([
@@ -61,7 +64,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
61
64
  }),
62
65
  menu.action({
63
66
  prefix: CopyIcon(),
64
- name: 'Copy',
67
+ name: translateKey(this.std, ...TOOLBAR_COPY),
65
68
  select: () => {
66
69
  const slice = Slice.fromModels(this.store, [this.model]);
67
70
  this.std.clipboard
@@ -79,7 +82,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
79
82
  class: {
80
83
  'delete-item': true,
81
84
  },
82
- name: 'Delete Database',
85
+ name: translateKey(this.std, ...DATABASE_DELETE),
83
86
  select: () => {
84
87
  this.model.children.slice().forEach(block => {
85
88
  this.store.deleteBlock(block);
@@ -146,6 +149,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
146
149
  ${renderUniLit(this.toolsWidget, props)}
147
150
  </div>
148
151
  ${renderUniLit(widgetPresets.quickSettingBar, props)}
152
+ ${this.renderReadOnlyNotice()}
149
153
  <affine-database-values-banner
150
154
  .dataSource="${this.dataSource}"
151
155
  .std="${this.std}"
@@ -210,12 +214,6 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
210
214
  widgetPresets.tools.viewOptions,
211
215
  widgetPresets.tools.tableAddRow,
212
216
  ],
213
- calendar: [
214
- widgetPresets.tools.filter,
215
- widgetPresets.tools.search,
216
- widgetPresets.tools.viewOptions,
217
- widgetPresets.tools.tableAddRow,
218
- ],
219
217
  gallery: [
220
218
  widgetPresets.tools.filter,
221
219
  widgetPresets.tools.sort,
@@ -277,6 +275,77 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
277
275
  </div>
278
276
  </div>`;
279
277
  };
278
+ /**
279
+ * A database a host that requires a source must connect first
280
+ * (`sourceRequirement` is `'connect'`, ADR 0035 amendment of 2026-10-29):
281
+ * no source and no row, or a hosted source the host no longer resolves.
282
+ * Drawn in place of the tools and the body: no row, no "New", no cell to
283
+ * type or paste into, so nothing is written. A hosted block keeps its view
284
+ * bar, since another of its views may still read a source. The button
285
+ * reopens the creation popover over the block, for the current view; a
286
+ * read-only document, or a host without the bundle's creation menu, gets
287
+ * the notice alone. Takes precedence over "Source not found" (§7).
288
+ */
289
+ this.renderSourceRequired = (dataViewLogic) => {
290
+ const dataSource = this.dataSource;
291
+ const sourceMissing = dataSource instanceof HostedDataSourceBase &&
292
+ dataSource.sourceMissing$.value;
293
+ if (sourceRequirement(this.std, this.model, sourceMissing) !== 'connect') {
294
+ return undefined;
295
+ }
296
+ const hosted = this.model.props.externalSourceId !== undefined;
297
+ const creationMenu = this.std.getOptional(DatabaseCreationMenuIdentifier);
298
+ const connect = creationMenu && !this.store.readonly$.value
299
+ ? () => creationMenu.open({
300
+ std: this.std,
301
+ blockId: this.blockId,
302
+ anchor: this,
303
+ viewId: dataViewLogic.view.id,
304
+ })
305
+ : undefined;
306
+ return html `<div class="${databaseHeaderContainerStyles}">
307
+ <div class="${databaseTitleRowStyles}">
308
+ ${this.renderTitle(dataViewLogic)}
309
+ </div>
310
+ ${hosted
311
+ ? html `<div
312
+ class="${databaseToolbarRowStyles} ${databaseHeaderBarStyles}"
313
+ >
314
+ ${this.renderViewBar({ dataViewLogic })}
315
+ </div>`
316
+ : nothing}
317
+ </div>
318
+ <div
319
+ class="${databaseSourceMissingStyles}"
320
+ data-testid="database-source-required"
321
+ role="status"
322
+ >
323
+ <div>${translateKey(this.std, ...DATABASE_SOURCE_REQUIRED)}</div>
324
+ <div class="${databaseSourceMissingHintStyles}">
325
+ ${translateKey(this.std, ...DATABASE_SOURCE_REQUIRED_HINT)}
326
+ </div>
327
+ ${connect
328
+ ? html `<data-view-component-button
329
+ data-testid="database-source-required-connect"
330
+ hoverType="border"
331
+ .onClick="${connect}"
332
+ .text="${translateKey(this.std, ...DATABASE_CREATION_TITLE)}"
333
+ ></data-view-component-button>`
334
+ : nothing}
335
+ </div>`;
336
+ };
337
+ /** "Move into a database" is waiting for the host: one call at a time. */
338
+ this.moving$ = signal(false);
339
+ this.moveIntoSource = () => {
340
+ if (this.moving$.peek())
341
+ return;
342
+ this.moving$.value = true;
343
+ moveDatabaseRowsIntoSource(this.std, this.model)
344
+ .catch(console.error)
345
+ .finally(() => {
346
+ this.moving$.value = false;
347
+ });
348
+ };
280
349
  this.#useZeroWidth_accessor_storage = true;
281
350
  }
282
351
  _buildDataSource() {
@@ -303,6 +372,12 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
303
372
  },
304
373
  });
305
374
  }
375
+ // A view of a host database is deleted through the host's request
376
+ // (`deleteView`, ADR 0035 amendment of 2026-10-29); the data source
377
+ // reads the picker from its own scope, below the editor's.
378
+ const picker = this.std.getOptional(DataSourcePickerProvider);
379
+ if (picker)
380
+ dataSource.serviceSet(DataSourcePickerProvider, picker);
306
381
  // `{ database: false }` takes view creation away, never the views.
307
382
  if (!this.std.getOptional(DatabaseViewCreationIdentifier)) {
308
383
  dataSource.serviceSet(ViewCreationOffKey, true);
@@ -312,11 +387,6 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
312
387
  .forEach(config => {
313
388
  dataSource.serviceSet(ExternalGroupByConfigProvider(config.name), config);
314
389
  });
315
- this.std.provider
316
- .getAll(CalendarExternalSourceProvider)
317
- .forEach(source => {
318
- dataSource.serviceSet(CalendarExternalSourceProvider(source.id), source);
319
- });
320
390
  };
321
391
  const provider = this.std.getOptional(DatabaseDataSourceProvider);
322
392
  const dataSource = provider && this.model.props.externalSourceId
@@ -325,6 +395,12 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
325
395
  const id = initialViewId(this.model.id, viewId => dataSource.viewManager.viewGet(viewId) != null, this.std.getOptional(DatabaseInitialViewProvider));
326
396
  if (id)
327
397
  dataSource.viewManager.setCurrentView(id);
398
+ // A native database under a host that requires a source is never edited
399
+ // natively, whatever its row count (`sourceRequirement`).
400
+ if (dataSource instanceof DatabaseBlockDataSource &&
401
+ sourceRequirement(this.std, this.model) !== 'none') {
402
+ dataSource.locked$.value = true;
403
+ }
328
404
  return dataSource;
329
405
  }
330
406
  get dataSource() {
@@ -429,6 +505,40 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
429
505
  }
430
506
  return this.rootComponent;
431
507
  }
508
+ /**
509
+ * Native rows under a host that requires a source (`sourceRequirement` is
510
+ * `'read-only'`): the rows render, locked (`locked$`), with this notice and,
511
+ * where the host turns content into a source (`createDatabaseFrom`) and
512
+ * the document is writable, "Move into a database"; a spinner while the
513
+ * host answers.
514
+ */
515
+ renderReadOnlyNotice() {
516
+ if (sourceRequirement(this.std, this.model) !== 'read-only') {
517
+ return nothing;
518
+ }
519
+ const canMove = canTurnIntoSource(this.std) && !this.store.readonly$.value;
520
+ const moving = this.moving$.value;
521
+ return html `<div
522
+ class="${databaseSourceMissingStyles}"
523
+ data-testid="database-source-required-read-only"
524
+ role="status"
525
+ >
526
+ <div>${translateKey(this.std, ...DATABASE_SOURCE_REQUIRED)}</div>
527
+ <div class="${databaseSourceMissingHintStyles}">
528
+ ${translateKey(this.std, ...DATABASE_SOURCE_REQUIRED_READ_ONLY)}
529
+ </div>
530
+ ${canMove
531
+ ? html `<data-view-component-button
532
+ data-testid="database-source-required-move"
533
+ hoverType="border"
534
+ aria-busy="${moving}"
535
+ .icon="${moving ? LoadingIcon() : undefined}"
536
+ .onClick="${this.moveIntoSource}"
537
+ .text="${translateKey(this.std, ...DATABASE_MOVE_INTO_SOURCE)}"
538
+ ></data-view-component-button>`
539
+ : nothing}
540
+ </div>`;
541
+ }
432
542
  renderDatabaseOps() {
433
543
  if (this.dataSource.readonly$.value) {
434
544
  return nothing;
@@ -509,7 +619,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
509
619
  setSelection: this.setSelection,
510
620
  dataSource: this.dataSource,
511
621
  headerWidget: this.headerWidget,
512
- viewPlaceholder: this.renderSourceMissing,
622
+ viewPlaceholder: logic => this.renderSourceRequired(logic) ?? this.renderSourceMissing(logic),
513
623
  onDrag: this.onDrag,
514
624
  clipboard: this.std.clipboard,
515
625
  dnd: this.std.dnd,
@@ -2,6 +2,8 @@ import { unsafeCSSVarV2 } from '../affine-shared/theme/index.js';
2
2
  import { DatabaseListViewIcon } from '@blocksuite/icons/lit';
3
3
  import { BlockComponent } from '../std/index.js';
4
4
  import { css, html } from 'lit';
5
+ import { translateKey } from '../affine-shared/services/index.js';
6
+ import { DATABASE_DRAG_PREVIEW } from './translations.js';
5
7
  export class DatabaseDndPreviewBlockComponent extends BlockComponent {
6
8
  static { this.styles = css `
7
9
  .affine-database-preview-container {
@@ -39,7 +41,9 @@ export class DatabaseDndPreviewBlockComponent extends BlockComponent {
39
41
  >
40
42
  <div class="database-preview-content">
41
43
  ${DatabaseListViewIcon({ width: '24px', height: '24px' })}
42
- <span class="text">Database Block</span>
44
+ <span class="text"
45
+ >${translateKey(this.std, ...DATABASE_DRAG_PREVIEW)}</span
46
+ >
43
47
  </div>
44
48
  </div>`;
45
49
  }
@@ -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
  }
@@ -114,6 +114,23 @@ export declare abstract class HostedDataSourceBase extends DataSourceBase {
114
114
  propertyDataGet(propertyId: string): Record<string, unknown>;
115
115
  propertyDataTypeGet(propertyId: string): TypeInstance | undefined;
116
116
  featureFlags$: ReadonlySignal<DatabaseFlags>;
117
+ /**
118
+ * Refuses every edit on a writable store: a database with no source, under
119
+ * a host that requires one, shows its native rows read-only (ADR 0035
120
+ * amendment of 2026-10-29). Every write path of the view layer reads
121
+ * `readonly$` (cells, paste, drag, "New", view and property menus).
122
+ */
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>;
117
134
  readonly$: ReadonlySignal<boolean>;
118
135
  viewConverts: import("./index.js").ViewConvertConfig[];
119
136
  viewMetas: ViewMeta[];