@qoretechnologies/reqraft 0.10.55-pr.124.g43fa138 → 0.10.55

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 (375) hide show
  1. package/.claude/CLAUDE.md +25 -0
  2. package/.tasks/INDEX.md +1 -0
  3. package/.tasks/STORY_NETWORK_ISOLATION.md +98 -0
  4. package/README.md +1 -1
  5. package/design/FORM_ENGINE_COMPACT.md +34 -0
  6. package/design/FORM_ENGINE_EXPRESSIONS.md +110 -0
  7. package/design/FORM_ENGINE_FIELD_MIGRATION.md +155 -1
  8. package/design/IDE_INTEGRATION.md +13 -0
  9. package/design/RENDER_EXPRESSION_TRANSPORT.md +76 -10
  10. package/design/SELECT_DIALOG_FLOOR.md +86 -0
  11. package/design/SMART_EDITOR_UX.md +21 -0
  12. package/design/UNAVAILABLE_ALLOWED_VALUES.md +226 -0
  13. package/dist/components/composer/Composer.d.ts +8 -3
  14. package/dist/components/composer/Composer.d.ts.map +1 -1
  15. package/dist/components/composer/Composer.js.map +1 -1
  16. package/dist/components/dpqlEditor/DpqlEditor.d.ts.map +1 -1
  17. package/dist/components/dpqlEditor/DpqlEditor.js +3 -3
  18. package/dist/components/dpqlEditor/DpqlEditor.js.map +1 -1
  19. package/dist/components/dpqlEditor/dpqlHelpers.d.ts +6 -0
  20. package/dist/components/dpqlEditor/dpqlHelpers.d.ts.map +1 -1
  21. package/dist/components/dpqlEditor/dpqlHelpers.js +21 -2
  22. package/dist/components/dpqlEditor/dpqlHelpers.js.map +1 -1
  23. package/dist/components/dpqlEditor/dpqlProbe.d.ts +37 -0
  24. package/dist/components/dpqlEditor/dpqlProbe.d.ts.map +1 -0
  25. package/dist/components/dpqlEditor/dpqlProbe.js +203 -0
  26. package/dist/components/dpqlEditor/dpqlProbe.js.map +1 -0
  27. package/dist/components/dpqlEditor/dpqlTags.d.ts +8 -0
  28. package/dist/components/dpqlEditor/dpqlTags.d.ts.map +1 -1
  29. package/dist/components/dpqlEditor/dpqlTags.js +5 -1
  30. package/dist/components/dpqlEditor/dpqlTags.js.map +1 -1
  31. package/dist/components/dpqlEditor/index.d.ts +4 -0
  32. package/dist/components/dpqlEditor/index.d.ts.map +1 -1
  33. package/dist/components/dpqlEditor/index.js +5 -1
  34. package/dist/components/dpqlEditor/index.js.map +1 -1
  35. package/dist/components/dpqlEditor/types.d.ts +42 -2
  36. package/dist/components/dpqlEditor/types.d.ts.map +1 -1
  37. package/dist/components/dpqlEditor/types.js +0 -4
  38. package/dist/components/dpqlEditor/types.js.map +1 -1
  39. package/dist/components/dpqlEditor/useDpqlProbe.d.ts +4 -0
  40. package/dist/components/dpqlEditor/useDpqlProbe.d.ts.map +1 -0
  41. package/dist/components/dpqlEditor/useDpqlProbe.js +29 -0
  42. package/dist/components/dpqlEditor/useDpqlProbe.js.map +1 -0
  43. package/dist/components/dpqlEditor/useDpqlSession.d.ts +5 -2
  44. package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
  45. package/dist/components/dpqlEditor/useDpqlSession.js +8 -2
  46. package/dist/components/dpqlEditor/useDpqlSession.js.map +1 -1
  47. package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
  48. package/dist/components/form/engine/CompactRow.js +414 -128
  49. package/dist/components/form/engine/CompactRow.js.map +1 -1
  50. package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
  51. package/dist/components/form/engine/CompactToolbar.js +5 -2
  52. package/dist/components/form/engine/CompactToolbar.js.map +1 -1
  53. package/dist/components/form/engine/FormEngine.d.ts +273 -22
  54. package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
  55. package/dist/components/form/engine/FormEngine.js +669 -192
  56. package/dist/components/form/engine/FormEngine.js.map +1 -1
  57. package/dist/components/form/engine/FormFieldsSkeleton.d.ts +48 -0
  58. package/dist/components/form/engine/FormFieldsSkeleton.d.ts.map +1 -0
  59. package/dist/components/form/engine/FormFieldsSkeleton.js +41 -0
  60. package/dist/components/form/engine/FormFieldsSkeleton.js.map +1 -0
  61. package/dist/components/form/engine/OptionFieldMessages.d.ts +108 -2
  62. package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
  63. package/dist/components/form/engine/OptionFieldMessages.js +203 -13
  64. package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
  65. package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
  66. package/dist/components/form/engine/_structuredData/SchemaDataView.js +68 -5
  67. package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
  68. package/dist/components/form/engine/compactRowContext.d.ts +15 -1
  69. package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
  70. package/dist/components/form/engine/compactRowContext.js.map +1 -1
  71. package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
  72. package/dist/components/form/engine/compactRowStyles.js +55 -29
  73. package/dist/components/form/engine/compactRowStyles.js.map +1 -1
  74. package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
  75. package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
  76. package/dist/components/form/engine/optionActions.d.ts +37 -1
  77. package/dist/components/form/engine/optionActions.d.ts.map +1 -1
  78. package/dist/components/form/engine/optionActions.js +71 -1
  79. package/dist/components/form/engine/optionActions.js.map +1 -1
  80. package/dist/components/form/engine/optionsContext.d.ts +22 -0
  81. package/dist/components/form/engine/optionsContext.d.ts.map +1 -0
  82. package/dist/components/form/engine/optionsContext.js +20 -0
  83. package/dist/components/form/engine/optionsContext.js.map +1 -0
  84. package/dist/components/form/engine/readFirst.d.ts +42 -1
  85. package/dist/components/form/engine/readFirst.d.ts.map +1 -1
  86. package/dist/components/form/engine/readFirst.js +76 -8
  87. package/dist/components/form/engine/readFirst.js.map +1 -1
  88. package/dist/components/form/engine/rendererTypes.d.ts +42 -3
  89. package/dist/components/form/engine/rendererTypes.d.ts.map +1 -1
  90. package/dist/components/form/engine/rendererTypes.js +3 -1
  91. package/dist/components/form/engine/rendererTypes.js.map +1 -1
  92. package/dist/components/form/engine/rowFocus.d.ts +30 -0
  93. package/dist/components/form/engine/rowFocus.d.ts.map +1 -0
  94. package/dist/components/form/engine/rowFocus.js +93 -0
  95. package/dist/components/form/engine/rowFocus.js.map +1 -0
  96. package/dist/components/form/engine/rowMenuContext.d.ts +78 -0
  97. package/dist/components/form/engine/rowMenuContext.d.ts.map +1 -0
  98. package/dist/components/form/engine/rowMenuContext.js +103 -0
  99. package/dist/components/form/engine/rowMenuContext.js.map +1 -0
  100. package/dist/components/form/engine/rowOpenPicker.d.ts +27 -0
  101. package/dist/components/form/engine/rowOpenPicker.d.ts.map +1 -0
  102. package/dist/components/form/engine/rowOpenPicker.js +30 -0
  103. package/dist/components/form/engine/rowOpenPicker.js.map +1 -0
  104. package/dist/components/form/engine/typeChoices.d.ts +42 -0
  105. package/dist/components/form/engine/typeChoices.d.ts.map +1 -0
  106. package/dist/components/form/engine/typeChoices.js +33 -0
  107. package/dist/components/form/engine/typeChoices.js.map +1 -0
  108. package/dist/components/form/expressions/DpqlRendering.d.ts +26 -0
  109. package/dist/components/form/expressions/DpqlRendering.d.ts.map +1 -0
  110. package/dist/components/form/expressions/DpqlRendering.js +53 -0
  111. package/dist/components/form/expressions/DpqlRendering.js.map +1 -0
  112. package/dist/components/form/expressions/ExpressionField.d.ts +7 -1
  113. package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
  114. package/dist/components/form/expressions/ExpressionField.js +203 -45
  115. package/dist/components/form/expressions/ExpressionField.js.map +1 -1
  116. package/dist/components/form/expressions/argumentPresence.d.ts +84 -0
  117. package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -0
  118. package/dist/components/form/expressions/argumentPresence.js +217 -0
  119. package/dist/components/form/expressions/argumentPresence.js.map +1 -0
  120. package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
  121. package/dist/components/form/expressions/builder/argumentLabel.js +9 -4
  122. package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
  123. package/dist/components/form/expressions/builder/index.d.ts +15 -2
  124. package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
  125. package/dist/components/form/expressions/builder/index.js +21 -65
  126. package/dist/components/form/expressions/builder/index.js.map +1 -1
  127. package/dist/components/form/expressions/builder/renderTemplate.d.ts +4 -1
  128. package/dist/components/form/expressions/builder/renderTemplate.d.ts.map +1 -1
  129. package/dist/components/form/expressions/builder/renderTemplate.js +10 -67
  130. package/dist/components/form/expressions/builder/renderTemplate.js.map +1 -1
  131. package/dist/components/form/expressions/mockExpressions.d.ts.map +1 -1
  132. package/dist/components/form/expressions/mockExpressions.js +8 -4
  133. package/dist/components/form/expressions/mockExpressions.js.map +1 -1
  134. package/dist/components/form/expressions/renderExpressionToText.d.ts +1 -1
  135. package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
  136. package/dist/components/form/expressions/renderExpressionToText.js +95 -19
  137. package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
  138. package/dist/components/form/expressions/types.d.ts +17 -2
  139. package/dist/components/form/expressions/types.d.ts.map +1 -1
  140. package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
  141. package/dist/components/form/expressions/useExpressions.js +5 -2
  142. package/dist/components/form/expressions/useExpressions.js.map +1 -1
  143. package/dist/components/form/expressions/useRenderExpression.d.ts +7 -16
  144. package/dist/components/form/expressions/useRenderExpression.d.ts.map +1 -1
  145. package/dist/components/form/expressions/useRenderExpression.js +50 -126
  146. package/dist/components/form/expressions/useRenderExpression.js.map +1 -1
  147. package/dist/components/form/fields/Field.d.ts +21 -0
  148. package/dist/components/form/fields/Field.d.ts.map +1 -1
  149. package/dist/components/form/fields/Field.js +54 -8
  150. package/dist/components/form/fields/Field.js.map +1 -1
  151. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +11 -1
  152. package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
  153. package/dist/components/form/fields/allowed-values/AllowedValues.js +52 -8
  154. package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
  155. package/dist/components/form/fields/auto/AutoFormField.d.ts +7 -1
  156. package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
  157. package/dist/components/form/fields/auto/AutoFormField.js +91 -28
  158. package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
  159. package/dist/components/form/fields/date/Date.d.ts +1 -1
  160. package/dist/components/form/fields/date/Date.d.ts.map +1 -1
  161. package/dist/components/form/fields/date/Date.js +16 -2
  162. package/dist/components/form/fields/date/Date.js.map +1 -1
  163. package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
  164. package/dist/components/form/fields/long-string/LongString.js +14 -2
  165. package/dist/components/form/fields/long-string/LongString.js.map +1 -1
  166. package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
  167. package/dist/components/form/fields/multi-select/MultiSelectFormField.js +50 -4
  168. package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
  169. package/dist/components/form/fields/number/Number.d.ts +12 -1
  170. package/dist/components/form/fields/number/Number.d.ts.map +1 -1
  171. package/dist/components/form/fields/number/Number.js +6 -3
  172. package/dist/components/form/fields/number/Number.js.map +1 -1
  173. package/dist/components/form/fields/rich-text/RichText.d.ts +47 -2
  174. package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
  175. package/dist/components/form/fields/rich-text/RichText.js +153 -12
  176. package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
  177. package/dist/components/form/fields/select/Select.d.ts.map +1 -1
  178. package/dist/components/form/fields/select/Select.js +120 -20
  179. package/dist/components/form/fields/select/Select.js.map +1 -1
  180. package/dist/components/form/fields/select/SelectCollection.d.ts +166 -9
  181. package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
  182. package/dist/components/form/fields/select/SelectCollection.js +200 -7
  183. package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
  184. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +2 -1
  185. package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
  186. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -2
  187. package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
  188. package/dist/components/form/fields/template/TemplateField.d.ts +34 -2
  189. package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
  190. package/dist/components/form/fields/template/TemplateField.js +606 -58
  191. package/dist/components/form/fields/template/TemplateField.js.map +1 -1
  192. package/dist/components/form/fields/template/TemplateText.d.ts +36 -0
  193. package/dist/components/form/fields/template/TemplateText.d.ts.map +1 -0
  194. package/dist/components/form/fields/template/TemplateText.js +80 -0
  195. package/dist/components/form/fields/template/TemplateText.js.map +1 -0
  196. package/dist/components/form/index.d.ts +2 -0
  197. package/dist/components/form/index.d.ts.map +1 -1
  198. package/dist/components/form/index.js +4 -0
  199. package/dist/components/form/index.js.map +1 -1
  200. package/dist/components/smartEditor/SmartEditor.d.ts.map +1 -1
  201. package/dist/components/smartEditor/SmartEditor.js +29 -8
  202. package/dist/components/smartEditor/SmartEditor.js.map +1 -1
  203. package/dist/components/smartEditor/useLspSession.d.ts +5 -1
  204. package/dist/components/smartEditor/useLspSession.d.ts.map +1 -1
  205. package/dist/components/smartEditor/useLspSession.js +16 -2
  206. package/dist/components/smartEditor/useLspSession.js.map +1 -1
  207. package/dist/components/supportTicket/TicketReplyBox.d.ts +8 -4
  208. package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -1
  209. package/dist/components/supportTicket/TicketReplyBox.js.map +1 -1
  210. package/dist/helpers/common.d.ts.map +1 -1
  211. package/dist/helpers/common.js +7 -3
  212. package/dist/helpers/common.js.map +1 -1
  213. package/dist/helpers/dpqlDetection.d.ts +58 -0
  214. package/dist/helpers/dpqlDetection.d.ts.map +1 -0
  215. package/dist/helpers/dpqlDetection.js +164 -0
  216. package/dist/helpers/dpqlDetection.js.map +1 -0
  217. package/dist/helpers/optionUiTypes.d.ts +31 -3
  218. package/dist/helpers/optionUiTypes.d.ts.map +1 -1
  219. package/dist/helpers/optionUiTypes.js +36 -1
  220. package/dist/helpers/optionUiTypes.js.map +1 -1
  221. package/dist/helpers/options.d.ts +57 -0
  222. package/dist/helpers/options.d.ts.map +1 -1
  223. package/dist/helpers/options.js +97 -1
  224. package/dist/helpers/options.js.map +1 -1
  225. package/dist/helpers/templateItems.d.ts +47 -0
  226. package/dist/helpers/templateItems.d.ts.map +1 -0
  227. package/dist/helpers/templateItems.js +88 -0
  228. package/dist/helpers/templateItems.js.map +1 -0
  229. package/dist/helpers/templateText.d.ts +80 -0
  230. package/dist/helpers/templateText.d.ts.map +1 -0
  231. package/dist/helpers/templateText.js +291 -0
  232. package/dist/helpers/templateText.js.map +1 -0
  233. package/dist/helpers/templates.d.ts +30 -12
  234. package/dist/helpers/templates.d.ts.map +1 -1
  235. package/dist/helpers/templates.js +91 -33
  236. package/dist/helpers/templates.js.map +1 -1
  237. package/dist/helpers/validations.d.ts +35 -8
  238. package/dist/helpers/validations.d.ts.map +1 -1
  239. package/dist/helpers/validations.js +162 -25
  240. package/dist/helpers/validations.js.map +1 -1
  241. package/dist/hooks/useArgSchema.d.ts +2 -1
  242. package/dist/hooks/useArgSchema.d.ts.map +1 -1
  243. package/dist/hooks/useFetch/useFetch.d.ts +3 -1
  244. package/dist/hooks/useFetch/useFetch.d.ts.map +1 -1
  245. package/dist/hooks/useFetch/useFetch.js +47 -9
  246. package/dist/hooks/useFetch/useFetch.js.map +1 -1
  247. package/dist/hooks/useTemplates.d.ts +20 -0
  248. package/dist/hooks/useTemplates.d.ts.map +1 -1
  249. package/dist/hooks/useTemplates.js +34 -2
  250. package/dist/hooks/useTemplates.js.map +1 -1
  251. package/dist/index.d.ts +2 -1
  252. package/dist/index.d.ts.map +1 -1
  253. package/dist/index.js +5 -1
  254. package/dist/index.js.map +1 -1
  255. package/dist/utils/fetch.d.ts +151 -1
  256. package/dist/utils/fetch.d.ts.map +1 -1
  257. package/dist/utils/fetch.js +256 -44
  258. package/dist/utils/fetch.js.map +1 -1
  259. package/dist/utils/lspClient.d.ts +6 -0
  260. package/dist/utils/lspClient.d.ts.map +1 -1
  261. package/dist/utils/lspClient.js +62 -11
  262. package/dist/utils/lspClient.js.map +1 -1
  263. package/dist/utils/websocket.d.ts +6 -0
  264. package/dist/utils/websocket.d.ts.map +1 -1
  265. package/dist/utils/websocket.js +15 -7
  266. package/dist/utils/websocket.js.map +1 -1
  267. package/package.json +10 -6
  268. package/src/components/composer/Composer.tsx +7 -2
  269. package/src/components/dpqlEditor/DpqlEditor.stories.tsx +3 -119
  270. package/src/components/dpqlEditor/DpqlEditor.tsx +5 -2
  271. package/src/components/dpqlEditor/dpqlHelpers.ts +20 -1
  272. package/src/components/dpqlEditor/dpqlProbe.ts +165 -0
  273. package/src/components/dpqlEditor/dpqlTags.ts +13 -0
  274. package/src/components/dpqlEditor/index.ts +4 -0
  275. package/src/components/dpqlEditor/types.ts +38 -2
  276. package/src/components/dpqlEditor/useDpqlProbe.ts +33 -0
  277. package/src/components/dpqlEditor/useDpqlSession.ts +20 -4
  278. package/src/components/form/engine/CompactRow.tsx +487 -160
  279. package/src/components/form/engine/CompactToolbar.tsx +10 -3
  280. package/src/components/form/engine/FormEngine.stories.tsx +1269 -201
  281. package/src/components/form/engine/FormEngine.tsx +922 -270
  282. package/src/components/form/engine/FormEngineStickyHeaders.stories.tsx +331 -0
  283. package/src/components/form/engine/FormFieldsSkeleton.tsx +77 -0
  284. package/src/components/form/engine/OptionFieldMessages.tsx +300 -14
  285. package/src/components/form/engine/_structuredData/SchemaDataView.tsx +94 -3
  286. package/src/components/form/engine/compactRowContext.ts +15 -6
  287. package/src/components/form/engine/compactRowStyles.ts +56 -2
  288. package/src/components/form/engine/compactToolbarContext.ts +1 -1
  289. package/src/components/form/engine/optionActions.ts +89 -1
  290. package/src/components/form/engine/optionsContext.ts +24 -0
  291. package/src/components/form/engine/readFirst.ts +88 -8
  292. package/src/components/form/engine/rendererTypes.ts +48 -4
  293. package/src/components/form/engine/rowFocus.ts +101 -0
  294. package/src/components/form/engine/rowMenuContext.ts +159 -0
  295. package/src/components/form/engine/rowOpenPicker.ts +27 -0
  296. package/src/components/form/engine/typeChoices.ts +63 -0
  297. package/src/components/form/expressions/DpqlRendering.tsx +85 -0
  298. package/src/components/form/expressions/ExpressionField.stories.tsx +826 -48
  299. package/src/components/form/expressions/ExpressionField.tsx +269 -55
  300. package/src/components/form/expressions/argumentPresence.ts +213 -0
  301. package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +38 -21
  302. package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
  303. package/src/components/form/expressions/builder/index.tsx +39 -81
  304. package/src/components/form/expressions/builder/renderTemplate.tsx +23 -56
  305. package/src/components/form/expressions/dpqlMockLanguage.ts +565 -0
  306. package/src/components/form/expressions/dpqlMockLsp.ts +109 -83
  307. package/src/components/form/expressions/mockExpressions.ts +8 -4
  308. package/src/components/form/expressions/renderExpressionToText.ts +112 -22
  309. package/src/components/form/expressions/types.ts +17 -2
  310. package/src/components/form/expressions/useExpressions.ts +5 -1
  311. package/src/components/form/expressions/useRenderExpression.ts +43 -127
  312. package/src/components/form/fields/Field.tsx +102 -20
  313. package/src/components/form/fields/allowed-values/AllowedValues.tsx +106 -44
  314. package/src/components/form/fields/auto/AutoFormField.stories.tsx +85 -18
  315. package/src/components/form/fields/auto/AutoFormField.tsx +97 -21
  316. package/src/components/form/fields/date/Date.tsx +17 -1
  317. package/src/components/form/fields/long-string/LongString.tsx +15 -3
  318. package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +65 -6
  319. package/src/components/form/fields/number/Number.tsx +25 -0
  320. package/src/components/form/fields/rich-text/RichText.tsx +248 -30
  321. package/src/components/form/fields/select/Select.stories.tsx +392 -0
  322. package/src/components/form/fields/select/Select.tsx +164 -27
  323. package/src/components/form/fields/select/SelectCollection.tsx +350 -6
  324. package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
  325. package/src/components/form/fields/template/TemplateField.stories.tsx +93 -14
  326. package/src/components/form/fields/template/TemplateField.tsx +825 -131
  327. package/src/components/form/fields/template/TemplateText.tsx +115 -0
  328. package/src/components/form/index.tsx +4 -0
  329. package/src/components/log/Log.stories.tsx +2 -1
  330. package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +3 -3
  331. package/src/components/smartEditor/SmartEditor.tsx +41 -9
  332. package/src/components/smartEditor/__fixtures__/mockLspServer.ts +52 -3
  333. package/src/components/smartEditor/useLspSession.ts +21 -3
  334. package/src/components/supportTicket/TicketReplyBox.tsx +8 -4
  335. package/src/components/ticketThread/TicketThread.stories.tsx +1 -0
  336. package/src/helpers/common.ts +7 -3
  337. package/src/helpers/dpqlDetection.ts +209 -0
  338. package/src/helpers/optionUiTypes.ts +38 -0
  339. package/src/helpers/options.ts +96 -0
  340. package/src/helpers/templateItems.ts +81 -0
  341. package/src/helpers/templateText.ts +350 -0
  342. package/src/helpers/templates.ts +94 -41
  343. package/src/helpers/validations.ts +203 -51
  344. package/src/hooks/useFetch/useFetch.stories.tsx +6 -5
  345. package/src/hooks/useFetch/useFetch.tsx +53 -7
  346. package/src/hooks/useStorage/useStorage.stories.tsx +2 -1
  347. package/src/hooks/useTemplates.ts +41 -0
  348. package/src/hooks/useWebSocket/useWebsocket.stories.tsx +2 -1
  349. package/src/index.tsx +14 -1
  350. package/src/stores/currentUser/currentUser.stories.tsx +2 -1
  351. package/src/stories/storyNetwork.ts +92 -0
  352. package/src/utils/fetch.ts +311 -21
  353. package/src/utils/lspClient.ts +68 -11
  354. package/src/utils/websocket.ts +20 -9
  355. package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts +0 -73
  356. package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts.map +0 -1
  357. package/dist/components/form/engine/__fixtures__/basicFormOptions.js +0 -317
  358. package/dist/components/form/engine/__fixtures__/basicFormOptions.js.map +0 -1
  359. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts +0 -41
  360. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts.map +0 -1
  361. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js +0 -45
  362. package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js.map +0 -1
  363. package/dist/components/form/expressions/dpqlMockLsp.d.ts +0 -3
  364. package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +0 -1
  365. package/dist/components/form/expressions/dpqlMockLsp.js +0 -91
  366. package/dist/components/form/expressions/dpqlMockLsp.js.map +0 -1
  367. package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts +0 -2
  368. package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts.map +0 -1
  369. package/dist/components/form/fields/template/__fixtures__/longExampleValue.js +0 -9
  370. package/dist/components/form/fields/template/__fixtures__/longExampleValue.js.map +0 -1
  371. package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts +0 -55
  372. package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +0 -1
  373. package/dist/components/smartEditor/__fixtures__/mockLspServer.js +0 -126
  374. package/dist/components/smartEditor/__fixtures__/mockLspServer.js.map +0 -1
  375. package/dist/stories/Data/fileFieldArgSchema.json +0 -86
@@ -96,6 +96,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
96
96
  exports.CompactRow = exports.richtextItemText = void 0;
97
97
  var jsx_runtime_1 = require("react/jsx-runtime");
98
98
  var reqore_1 = require("@qoretechnologies/reqore");
99
+ var rowMenuContext_1 = require("./rowMenuContext");
99
100
  var optionActions_1 = require("./optionActions");
100
101
  var flatten_1 = __importDefault(require("lodash/flatten"));
101
102
  var isEqual_1 = __importDefault(require("lodash/isEqual"));
@@ -105,7 +106,9 @@ var use_context_selector_1 = require("use-context-selector");
105
106
  var validations_1 = require("../../../helpers/validations");
106
107
  var templates_1 = require("../../../helpers/templates");
107
108
  var common_1 = require("../../../helpers/common");
109
+ var templateText_1 = require("../../../helpers/templateText");
108
110
  var ReadOnlyTemplateTag_1 = require("../fields/template/ReadOnlyTemplateTag");
111
+ var TemplateText_1 = require("../fields/template/TemplateText");
109
112
  var codeSize_1 = require("../../codeSize");
110
113
  var Description_1 = require("../../Description");
111
114
  var markdownRendererContext_1 = require("../../Description/markdownRendererContext");
@@ -113,10 +116,13 @@ var FocusedEditing_1 = require("../../FocusedEditing");
113
116
  var compactRowContext_1 = require("./compactRowContext");
114
117
  var compactRowStyles_1 = require("./compactRowStyles");
115
118
  var OptionFieldMessages_1 = require("./OptionFieldMessages");
119
+ var rowFocus_1 = require("./rowFocus");
120
+ var rowOpenPicker_1 = require("./rowOpenPicker");
116
121
  var SchemaDataView_1 = require("./_structuredData/SchemaDataView");
117
122
  var fetch_1 = require("../../../utils/fetch");
118
123
  var readFirst_1 = require("./readFirst");
119
124
  var StructuredDataView_1 = require("./_structuredData/StructuredDataView");
125
+ var DpqlRendering_1 = require("../expressions/DpqlRendering");
120
126
  /**
121
127
  * Does this markdown value have more document in it than one row can show?
122
128
  *
@@ -170,11 +176,6 @@ var COMPACT_SINGLE_VALUE_TYPES = new Set([
170
176
  'timeout',
171
177
  'method-name',
172
178
  ]);
173
- // How many consumer-injected actions may sit inline in a row before the rest
174
- // overflow into the row's menu. A row's action slot shares space with the
175
- // value; an unbounded button strip would squeeze the value out, and the
176
- // consumer controls how many actions it injects.
177
- var MAX_INLINE_OPTION_ACTIONS = 2;
178
179
  // One read-first row: label | value | action collapsed; the real editor (the
179
180
  // classic renderOption) expanded. `hidden` = search-surfaced optional —
180
181
  // activating the row adds the field first.
@@ -199,8 +200,8 @@ var richtextItemText = function (item) {
199
200
  exports.richtextItemText = richtextItemText;
200
201
  exports.CompactRow = (0, react_1.memo)(function (_a) {
201
202
  var _b;
202
- var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
203
- var optionName = _a.optionName, optionField = _a.optionField, _o = _a.hidden, hidden = _o === void 0 ? false : _o, _p = _a.clustered, clustered = _p === void 0 ? false : _p, _q = _a.clusterFirst, clusterFirst = _q === void 0 ? false : _q, _r = _a.clusterLast, clusterLast = _r === void 0 ? false : _r, absorbedFields = _a.absorbedFields;
203
+ var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
204
+ var optionName = _a.optionName, optionField = _a.optionField, _r = _a.hidden, hidden = _r === void 0 ? false : _r, _s = _a.clustered, clustered = _s === void 0 ? false : _s, _t = _a.clusterFirst, clusterFirst = _t === void 0 ? false : _t, _u = _a.clusterLast, clusterLast = _u === void 0 ? false : _u, absorbedFields = _a.absorbedFields;
204
205
  var readOnly = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.readOnly; });
205
206
  var commitMode = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.commitMode; });
206
207
  var options = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.options; });
@@ -221,7 +222,6 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
221
222
  var isExpanded = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) {
222
223
  return v.expandedOptions.includes(optionName);
223
224
  });
224
- var autoFocusNameRef = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.autoFocusNameRef; });
225
225
  var isHighlighted = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) {
226
226
  return v.highlightedOptions.includes(optionName);
227
227
  });
@@ -231,6 +231,38 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
231
231
  var setHighlightedOptions = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.setHighlightedOptions; });
232
232
  var setFocusedEditing = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.setFocusedEditing; });
233
233
  var readRowHeights = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.readRowHeights; });
234
+ var onReadOnlyActivate = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.onReadOnlyActivate; });
235
+ /**
236
+ * The floor recorded when a row is opened lasts until the author changes
237
+ * something.
238
+ *
239
+ * `activate()` measures the row as it was READ and pins that as the
240
+ * editing row's `min-height`, so opening a row cannot yank the content
241
+ * below it up under the pointer. That is right for the transition and
242
+ * wrong for the rest of the session: a row that was tall because it
243
+ * carried a warning keeps that height after the author fixes the very
244
+ * thing the warning was about, leaving a large empty box where the
245
+ * explanation used to be — reported as "the chip is removed, but the
246
+ * vertical size of the option remains the same".
247
+ *
248
+ * Released on the first change to the value, which is exactly the moment
249
+ * the author has acted and the row should settle to what it now holds.
250
+ * Nothing is yanked by that: the author caused it and is looking at it.
251
+ *
252
+ * Keyed on a STRING rather than the value itself. A form value is often a
253
+ * fresh object on every render (`{source, value}` for a reference), so a
254
+ * dependency on its identity would drop the floor on the first re-render
255
+ * and the transition it exists for would never be smoothed at all.
256
+ */
257
+ var valueKey = (optionField === null || optionField === void 0 ? void 0 : optionField.value) === null || typeof (optionField === null || optionField === void 0 ? void 0 : optionField.value) !== 'object' ?
258
+ String(optionField === null || optionField === void 0 ? void 0 : optionField.value)
259
+ : JSON.stringify(optionField === null || optionField === void 0 ? void 0 : optionField.value);
260
+ /* `valueKey` alone, deliberately - see above. No eslint-disable: this repo
261
+ does not load `react-hooks`, so a suppression for a rule that is not
262
+ there is itself an error, and the pre-push lint refuses the push. */
263
+ react_1.default.useEffect(function () {
264
+ delete readRowHeights.current[optionName];
265
+ }, [valueKey]);
234
266
  var originalValue = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.originalValue; });
235
267
  var availableOptions = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.availableOptions; });
236
268
  var requiredGroupsInfo = (0, use_context_selector_1.useContextSelector)(compactRowContext_1.CompactRowContext, function (v) { return v.requiredGroupsInfo; });
@@ -258,37 +290,43 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
258
290
  // renderer when it supplied one, reqraft's own otherwise.
259
291
  // Value-cell content: colour adds a swatch, file an icon + size; hash keeps
260
292
  // its "N fields" summary (sub-fields reveal beneath the row).
261
- /**
262
- * Flattens the line breaks in one prose segment of a richtext SUMMARY.
263
- *
264
- * The read-first row is a single line: every other value type reaches it
265
- * through `whiteSpace: 'nowrap'`, which collapses newlines for free. The
266
- * richtext branch is the only one that opts into `'pre'` — it has to, or the
267
- * spaces that separate a word from the chip beside it are dropped — and `pre`
268
- * also honours the newlines, which `nowrap` would have eaten.
269
- *
270
- * So a genuinely multi-line value (an alert rule's Gmail message body is five
271
- * `\n`-separated lines) gave each segment after the first a blank first line.
272
- * The wrapper centres its items, so a two-line-tall box centred against
273
- * one-line chips put every word 12px below the chip beside it, and the row
274
- * read as a staircase. Measured on supah: prose boxes 30px against 14px chips.
275
- *
276
- * A space, not nothing: consecutive prose segments are merged before they get
277
- * here, so a value with a break and no chip between its lines would otherwise
278
- * lose the word boundary entirely.
279
- */
280
- var collapseSummaryBreaks = function (text) { return text.replace(/\s*\r?\n\s*/g, ' '); };
281
293
  var renderReadFirstValue = function (field, schema, formatted,
282
294
  // An editable row gets one line and an ellipsis — its value line is a
283
295
  // summary you click to open. A read-only row is the only rendering the
284
296
  // value gets, so it wraps.
285
297
  full) {
298
+ var _a;
286
299
  if (full === void 0) { full = false; }
287
300
  var valueType = (0, readFirst_1.getValueType)(field, schema);
288
301
  var wrapStyle = __assign({ display: 'inline-flex', alignItems: 'center', gap: 6, minWidth: 0, maxWidth: '100%' }, (full ? { flexWrap: 'wrap' } : {}));
289
302
  var textStyle = full ?
290
303
  { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }
291
304
  : { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' };
305
+ /* A row has to SAY when its value is an expression.
306
+
307
+ Reported from the live IDE: an author accepted the *Use as expression*
308
+ offer on one field and not on the other, and the two rows read exactly
309
+ the same - `1 + 2` and `2 + 1`, both plain text - so there was no way
310
+ to tell which had been converted. The assertion then compared the
311
+ string `"1 + 2"` against the number `3` and the author had no way to
312
+ see why.
313
+
314
+ The same Σ the expression editor uses, so the mark means the same thing
315
+ wherever it appears. */
316
+ var withExpressionMarker = function (rendered) {
317
+ if (!(field === null || field === void 0 ? void 0 : field.is_expression)) {
318
+ return rendered;
319
+ }
320
+ return ((0, jsx_runtime_1.jsxs)("span", { style: __assign(__assign({}, wrapStyle), { gap: 4 }), children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreIcon, { icon: 'Functions', size: 'tiny',
321
+ /* A stable hook: reqore renders every icon as a bare
322
+ `.reqore-icon`, so without this there is nothing to select the
323
+ mark by — in a test or in a consumer's stylesheet. */
324
+ className: 'reqraft-expression-marker',
325
+ /* Opacity through `effect`, which is how this repo dims a reqore
326
+ component (see the card's `short_desc` below); only the flex
327
+ rule, which reqore has no prop for, stays inline. */
328
+ effect: { opacity: 0.7 }, style: { flexShrink: 0 }, tooltip: 'This value is an expression' }), rendered] }));
329
+ };
292
330
  if (valueType === 'rgbcolor') {
293
331
  var swatch = (0, readFirst_1.colorToCss)(field === null || field === void 0 ? void 0 : field.value);
294
332
  return ((0, jsx_runtime_1.jsxs)("span", { style: swatch ? __assign(__assign({}, wrapStyle), {
@@ -307,6 +345,34 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
307
345
  var truthy = (field === null || field === void 0 ? void 0 : field.value) === true || (field === null || field === void 0 ? void 0 : field.value) === 'true';
308
346
  return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreTag, { size: 'small', minimal: true, intent: truthy ? 'success' : 'danger', label: formatted }));
309
347
  }
348
+ // A test's cases read as CHIPS here, the same as the names in the table
349
+ // this row opens (`CasesTable`, qorus-ide). `test-cases` is a
350
+ // renderer-only ui_type, so it reached none of the branches above and
351
+ // fell through to the generic summary, which flattens the array of case
352
+ // hashes to a comma-joined string — "case_1, resolved_expected_values".
353
+ // A case name is an identifier the author types into `$.` references and
354
+ // skip conditions, and a chip is how the rest of the product writes an
355
+ // identifier down; the table already agreed, so the collapsed row was the
356
+ // only surface still printing them as prose.
357
+ //
358
+ // `intent` is a FILL, which is what a tag is — the hazard the other
359
+ // branches warn about is only ever using an intent as a TEXT colour.
360
+ //
361
+ // Falls through to the plain summary unless EVERY entry yields a name: a
362
+ // partly-chipped row would read as though the unnamed cases were missing.
363
+ if (valueType === 'test-cases' && Array.isArray(field === null || field === void 0 ? void 0 : field.value)) {
364
+ var caseNames = field.value.map(function (item) {
365
+ var name = item === null || item === void 0 ? void 0 : item.name;
366
+ return typeof name === 'string' ? name.trim() : '';
367
+ });
368
+ if (caseNames.length > 0 && caseNames.every(function (name) { return name !== ''; })) {
369
+ return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreControlGroup, { gapSize: 'tiny', verticalAlign: 'center',
370
+ // An editable row is ONE line: further chips clip with the row
371
+ // instead of growing it. A read-only row is the only rendering
372
+ // the value gets, so it wraps and shows every case.
373
+ wrap: full, style: full ? undefined : { minWidth: 0, overflow: 'hidden' }, children: caseNames.map(function (name, index) { return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreTag, { className: 'options-readfirst-case-tag', size: 'small', minimal: true, intent: 'info', label: name }, "".concat(name, "-").concat(index))); }) }));
374
+ }
375
+ }
310
376
  // Code-editor read summary: replace the truncated string with a chip
311
377
  // showing line count + character count. The actual code renders below
312
378
  // the row as a monospace `<pre>` inside a `ReqoreCollapsibleContent` —
@@ -350,13 +416,35 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
350
416
  if (allowedImage) {
351
417
  return ((0, jsx_runtime_1.jsxs)("span", { style: wrapStyle, children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreIcon, { image: allowedImage, size: '16px', style: { flexShrink: 0 } }), (0, jsx_runtime_1.jsx)("span", { style: textStyle, children: formatted })] }));
352
418
  }
419
+ // The same, for an option whose mark is an ICON rather than a logo. Only
420
+ // images were drawn here, so a list whose entries are told apart by their
421
+ // icon — every interface kind is a bare word, `Service`, `Job`, `Qog` —
422
+ // showed the mark while choosing and lost it the moment the row went
423
+ // read-only, which is where a reader spends most of their time.
424
+ var allowedIcon = (0, readFirst_1.getAllowedValueIcon)(field === null || field === void 0 ? void 0 : field.value, schema);
425
+ if (allowedIcon) {
426
+ return ((0, jsx_runtime_1.jsxs)("span", { style: wrapStyle, children: [(0, jsx_runtime_1.jsx)(reqore_1.ReqoreIcon, { icon: allowedIcon, size: '16px', style: { flexShrink: 0 } }), (0, jsx_runtime_1.jsx)("span", { style: textStyle, children: formatted })] }));
427
+ }
353
428
  // Template value ($local:…): the read-only template picker — the SAME chip
354
429
  // TemplateField renders when disabled, so a template reads identically here
355
430
  // and in the editor (qorus-ide intent scheme: info / qorus purple).
431
+ //
432
+ // A field may also declare templates OF ITS OWN, and a value drawn from
433
+ // that list is just as much a template even when it does not look like
434
+ // `$name:key` — `isValueTemplate` is a guess at the shape of the built-in
435
+ // grammar, and a host's own grammar (a Qorus test's `$.order.id`) fails it.
436
+ // Asking the field's list whether it CONTAINS the value settles it without
437
+ // guessing at syntax, and hands back the entry whose label the chip prints,
438
+ // so a reference reads as the name it was chosen by here as well as in the
439
+ // editor rather than as a raw path.
440
+ var ownTemplates = (_a = schema === null || schema === void 0 ? void 0 : schema.templates) !== null && _a !== void 0 ? _a : templates;
441
+ // The grammar the field declares its references in, when it speaks one of
442
+ // its own — the same schema key its rich-text editor is given.
443
+ var templateGrammar = schema === null || schema === void 0 ? void 0 : schema.templateToken;
356
444
  if (typeof (field === null || field === void 0 ? void 0 : field.value) === 'string' &&
357
445
  !field.is_expression &&
358
- (0, templates_1.isValueTemplate)(field.value)) {
359
- return (0, jsx_runtime_1.jsx)(ReadOnlyTemplateTag_1.ReadOnlyTemplateTag, { value: field.value, templates: templates, size: 'small' });
446
+ ((0, templates_1.isValueTemplate)(field.value) || !!(0, templates_1.findTemplate)(ownTemplates !== null && ownTemplates !== void 0 ? ownTemplates : {}, field.value))) {
447
+ return (0, jsx_runtime_1.jsx)(ReadOnlyTemplateTag_1.ReadOnlyTemplateTag, { value: field.value, templates: ownTemplates, size: 'small' });
360
448
  }
361
449
  // A LIST whose elements are rich-text envelopes is a list of strings, so it
362
450
  // reads as those strings. Without this the row fell through to the generic
@@ -375,49 +463,38 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
375
463
  if (valueType === 'richtext' && Array.isArray(field === null || field === void 0 ? void 0 : field.value)) {
376
464
  var segments = (0, common_1.richtextToSegments)(field.value);
377
465
  if (segments.some(function (segment) { return segment.kind === 'tag'; })) {
378
- return ((0, jsx_runtime_1.jsx)("span", { style: __assign(__assign({}, wrapStyle), { gap: 4, overflow: 'hidden' }), children: segments.map(function (segment, index) {
379
- var _a;
380
- return segment.kind === 'tag' ?
381
- (0, jsx_runtime_1.jsx)(reqore_1.ReqoreTag, __assign({ size: 'tiny',
382
- // The chip's label has to be the same size as the prose it is
383
- // embedded in. The wrapper centres boxes, so two different text
384
- // sizes centred against each other cannot share a baseline, and
385
- // the chips visibly float above the words. Inheriting the row's
386
- // size makes centring align the baselines too, at whatever font
387
- // size the consuming app uses.
388
- style: { fontSize: 'inherit' }, icon: 'ExchangeDollarLine', label: segment.text || segment.value, tooltip: segment.value }, (0, templates_1.getTemplateTagStyle)((_a = (templates ? (0, templates_1.findTemplate)(templates, segment.value || '') : undefined)) === null || _a === void 0 ? void 0 : _a.metadata)), index)
389
- : (0, jsx_runtime_1.jsx)("span", { style: { whiteSpace: full ? 'pre-wrap' : 'pre' }, children: full ? segment.text : collapseSummaryBreaks(segment.text) }, index);
390
- }) }));
466
+ return (0, jsx_runtime_1.jsx)(TemplateText_1.TemplateText, { segments: segments, templates: ownTemplates, full: full });
391
467
  }
392
468
  }
393
- // An expression summarises to one line of text, and a template reference
394
- // inside it printed as its own raw token — `$data:{…}` renders a value as
395
- // code where a name belongs. Chip every reference here, on the richtext
396
- // branch's pattern above, so the wrapper (`trim(`…`)`) still reads as the
397
- // text it is while the reference reads as a name.
398
- //
399
- // Unconditionally: a surface with no catalogue (the Automation Hub
400
- // template preview never fetches one) still shows the reference's own
401
- // path, which is the point — a conditional chip is no chip at all
402
- // exactly where the raw token looks worst.
469
+ // An expression reads as DPQL wherever it is shown without being edited:
470
+ // the same rendering its Explain panel and Preview use — monospace,
471
+ // coloured by the language server, template references as chips — so a
472
+ // collapsed row and the editor it opens never disagree about what the
473
+ // value looks like. See `DpqlRendering`.
403
474
  if ((field === null || field === void 0 ? void 0 : field.is_expression) && formatted) {
404
- var segments = (0, templates_1.splitTemplateTokens)(formatted);
405
- var named = segments.map(function (segment) {
406
- return segment.kind === 'token' ? __assign(__assign({}, segment), (0, templates_1.describeTemplateReference)(templates, segment.text)) : segment;
407
- });
408
- if (named.some(function (segment) { return segment.kind === 'token'; })) {
409
- return ((0, jsx_runtime_1.jsx)("span", { style: __assign(__assign({}, wrapStyle), { gap: 4, overflow: 'hidden' }), children: named.map(function (segment, index) {
410
- var _a;
411
- return segment.kind === 'token' ?
412
- (0, jsx_runtime_1.jsx)(reqore_1.ReqoreTag, __assign({ size: 'tiny',
413
- // Inherit the row's font size so the chip shares a baseline
414
- // with the text around it — see the richtext branch above.
415
- style: { fontSize: 'inherit' }, icon: 'ExchangeDollarLine', label: segment.label, tooltip: segment.text }, (0, templates_1.getTemplateTagStyle)((_a = segment.item) === null || _a === void 0 ? void 0 : _a.metadata)), index)
416
- : (0, jsx_runtime_1.jsx)("span", { style: { whiteSpace: full ? 'pre-wrap' : 'pre' }, children: segment.text }, index);
417
- }) }));
418
- }
475
+ return withExpressionMarker((0, jsx_runtime_1.jsx)("span", { style: __assign({ minWidth: 0, flex: '1 1 auto' }, (full ? {} : { overflow: 'hidden' })), children: (0, jsx_runtime_1.jsx)(DpqlRendering_1.DpqlRendering, { text: formatted, templates: ownTemplates }) }));
419
476
  }
420
- return full ? (0, jsx_runtime_1.jsx)("span", { style: textStyle, children: formatted }) : formatted;
477
+ /* A value that MENTIONS a reference rather than being one.
478
+
479
+ The branch above answers "is this value a template?", and a predicate is
480
+ not: `$._case.mode != 'simulate'` is a comparison the author wrote with a
481
+ reference inside it. So it fell all the way through to the plain string
482
+ and the row printed the engine's own spelling of a value the author had
483
+ picked by name — the one thing the template mechanism exists to stop.
484
+ Reported against the Skip-when field of a test case, where the list
485
+ above the drawer already named it and the drawer contradicted it.
486
+
487
+ Drawn from `formatted` rather than from the raw value, so everything the
488
+ formatter already did — a joined list, a sensitive value's dots — still
489
+ holds, and only the references inside the resulting line become chips.
490
+ The author's own text around them survives exactly as typed. */
491
+ var templateSegments = typeof formatted === 'string' && formatted ?
492
+ (0, templateText_1.templateTextSegments)(formatted, ownTemplates, templateGrammar)
493
+ : [];
494
+ if (templateSegments.some(function (segment) { return segment.kind === 'tag'; })) {
495
+ return withExpressionMarker((0, jsx_runtime_1.jsx)(TemplateText_1.TemplateText, { segments: templateSegments, templates: ownTemplates, full: full }));
496
+ }
497
+ return withExpressionMarker(full ? (0, jsx_runtime_1.jsx)("span", { style: textStyle, children: formatted }) : formatted);
421
498
  };
422
499
  var schema = options === null || options === void 0 ? void 0 : options[optionName];
423
500
  var label = (schema === null || schema === void 0 ? void 0 : schema.display_name) || optionName;
@@ -431,33 +508,9 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
431
508
  : [];
432
509
  }, [availableOptions === null || availableOptions === void 0 ? void 0 : availableOptions[optionName], optionActions, optionName, schema]);
433
510
  // Which injected actions stay as inline buttons, and which move into the
434
- // overflow menu. Everything collapses on touch / narrow viewports — a
435
- // hover-gated button is unreachable without a hover — and anything past the
436
- // inline cap overflows regardless, so a consumer injecting ten actions can
437
- // never push the row's value out of view.
438
- var _s = react_1.default.useMemo(function () {
439
- return collapseOptionActions ?
440
- [[], injectedOptionActions]
441
- : [
442
- injectedOptionActions.slice(0, MAX_INLINE_OPTION_ACTIONS),
443
- injectedOptionActions.slice(MAX_INLINE_OPTION_ACTIONS),
444
- ];
445
- }, [collapseOptionActions, injectedOptionActions]), inlineOptionActions = _s[0], menuOptionActions = _s[1];
446
- var injectedOptionActionMenuItems = react_1.default.useMemo(function () {
447
- return menuOptionActions.map(function (action, index) {
448
- var _a, _b;
449
- return ({
450
- // A panel action labels itself with `label`, but an icon-only one
451
- // (the IDE's AI-assist button) carries its name in the tooltip —
452
- // a menu row has no hover affordance to fall back on.
453
- label: (_b = (_a = action.label) !== null && _a !== void 0 ? _a : action.tooltip) !== null && _b !== void 0 ? _b : "Action ".concat(index + 1),
454
- icon: action.icon,
455
- intent: action.intent,
456
- disabled: action.disabled,
457
- onClick: function () { var _a; return (_a = action.onClick) === null || _a === void 0 ? void 0 : _a.call(action); },
458
- });
459
- });
460
- }, [menuOptionActions]);
511
+ // overflow menu — see `splitOptionActions`.
512
+ var _v = react_1.default.useMemo(function () { return (0, optionActions_1.splitOptionActions)(injectedOptionActions, !!collapseOptionActions); }, [collapseOptionActions, injectedOptionActions]), inlineOptionActions = _v[0], menuOptionActions = _v[1];
513
+ var injectedOptionActionMenuItems = react_1.default.useMemo(function () { return menuOptionActions.map(optionActions_1.optionActionAsMenuItem); }, [menuOptionActions]);
461
514
  var renderInjectedOptionAction = function (action, index, size) {
462
515
  if (size === void 0) { size = 'small'; }
463
516
  // `show` drives visibility here instead of reaching the DOM. The classic
@@ -486,7 +539,22 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
486
539
  var changed = !hidden && !readOnly && hasOptionChanged(optionField === null || optionField === void 0 ? void 0 : optionField.value, optionName);
487
540
  // "Has a value" = set to anything non-empty. Drives the edit-row Clear
488
541
  // button and the cluster node's filled state (see `memberSet`).
489
- var hasValue = (optionField === null || optionField === void 0 ? void 0 : optionField.value) !== undefined && (optionField === null || optionField === void 0 ? void 0 : optionField.value) !== null && (optionField === null || optionField === void 0 ? void 0 : optionField.value) !== '';
542
+ /* "Is there anything to clear?" — this predicate has exactly one job, and
543
+ both of its uses are the Clear-value button.
544
+
545
+ An empty list is not a value. `[]` is neither `undefined`, `null` nor `''`,
546
+ so a field whose last item had just been removed went on offering to clear
547
+ it, and the confirmation asked whether to clear nothing. A test's cases
548
+ table is where this shows: delete the last case and a red clear-all sat
549
+ above an empty table, guarding a no-op.
550
+
551
+ Deliberately arrays only. An empty hash is arguable — "present but empty"
552
+ can be a real state for a hash — and nothing has demonstrated the same
553
+ defect there, so widening this predicate would be a guess. */
554
+ var hasValue = (optionField === null || optionField === void 0 ? void 0 : optionField.value) !== undefined &&
555
+ (optionField === null || optionField === void 0 ? void 0 : optionField.value) !== null &&
556
+ (optionField === null || optionField === void 0 ? void 0 : optionField.value) !== '' &&
557
+ !(Array.isArray(optionField.value) && optionField.value.length === 0);
490
558
  // Required-group membership shows a PERSISTENT chip on every member: amber
491
559
  // "One of" while the group is unmet (tap → flash siblings), then a muted-green
492
560
  // resolution once satisfied — "Covers" on the field that satisfied it,
@@ -531,7 +599,7 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
531
599
  : (schema === null || schema === void 0 ? void 0 : schema.ui_type) || (schema === null || schema === void 0 ? void 0 : schema.type))) !== null && _d !== void 0 ? _d : '';
532
600
  // Scalars edit in place inside the row; complex fields (tall or nested
533
601
  // editors) still open the expanded card below.
534
- var inlineEditable = !readOnly &&
602
+ var inlineEditableNow = !readOnly &&
535
603
  !(schema === null || schema === void 0 ? void 0 : schema.arg_schema) &&
536
604
  !(operators && (0, size_1.default)(operators)) &&
537
605
  // Expression fields open the builder/DPQL editor — too tall for inline.
@@ -539,22 +607,165 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
539
607
  // A choice with per-option logos (e.g. language) reads better collapsed.
540
608
  !(0, readFirst_1.optionHasImages)(schema) &&
541
609
  !COMPACT_COMPLEX_TYPES.has(editType);
542
- // Auto-focus the editor's first input when a field is opened, so you can type
543
- // straight away (matches the prototype's tap-to-edit feel).
610
+ /* Which of the two editors a row gets is settled when the row OPENS.
611
+ *
612
+ * Every term above reads the VALUE or a schema derived from it, so the
613
+ * answer can change while the author is inside the control — and the two
614
+ * branches are different subtrees, so a changed answer does not restyle
615
+ * the editor, it unmounts one and mounts the other. Everything the old one
616
+ * held goes with it: what was typed, the caret, and any state the editor
617
+ * keeps about how it was entered.
618
+ *
619
+ * `is_expression` is the term that actually fires, and it fires on the one
620
+ * interaction that exists to keep the author where they are: accepting
621
+ * "Use as expression" is the moment the value becomes an expression, and
622
+ * the expression shell opens on its Text view precisely because the author
623
+ * has just written the expression as text. The remount discarded that and
624
+ * dropped them into the visual builder, so the sentence they had typed was
625
+ * nowhere on screen. Clicking Text then appeared to work and bounced back,
626
+ * because the next value change remounted the row again.
627
+ *
628
+ * So the row keeps the editor it opened with for as long as it is open,
629
+ * and asks the question again when it is next opened — the same trade
630
+ * `editEntryValue` above makes for a different reason, and the one
631
+ * qorus-ide's `referenceEditorLatch` makes for this exact one. A field that
632
+ * already holds an expression when it is opened still gets the card; it is
633
+ * only a field that BECOMES one under the author that stays put.
634
+ *
635
+ * Held in a ref and settled during render rather than in an effect: an
636
+ * effect lands one render late, and one render late is a visible flash of
637
+ * the wrong editor — which is the thing being fixed.
638
+ */
639
+ var inlineEditableLatch = react_1.default.useRef(undefined);
640
+ /* Whether THIS opening was the author asking to change the value.
641
+ *
642
+ * A click (or Enter/Space) on a closed row means "let me edit this"; a row
643
+ * opened by the form itself — `initialExpandedOptions` from an address, the
644
+ * first-attention row, a diagram opening the step it is showing — means
645
+ * "here it is". Only the first answers the question the picker answers, so
646
+ * only the first opens one (see the caret effect below). A ref, because it
647
+ * is read by the effect that runs after the open and must not itself cause
648
+ * a render. */
649
+ var openedByAuthor = react_1.default.useRef(false);
650
+ if (!isExpanded) {
651
+ inlineEditableLatch.current = undefined;
652
+ }
653
+ else if (inlineEditableLatch.current === undefined) {
654
+ inlineEditableLatch.current = inlineEditableNow;
655
+ }
656
+ var inlineEditable = isExpanded ? !!inlineEditableLatch.current : inlineEditableNow;
657
+ /* The author's intent survives until the row is closed again, and no longer.
658
+ *
659
+ * An EFFECT, keyed on the row actually closing, rather than a line in the
660
+ * render body: between the click and the row opening there is a committed
661
+ * render that still reads `expandedOptions` without this row in it (the
662
+ * context propagates a beat after the handler runs), and clearing the flag
663
+ * on every collapsed render cleared it in that gap — the picker then never
664
+ * opened, which is the defect this whole mechanism exists to fix. Keyed on
665
+ * `isExpanded`, the transient render is not a change and the effect does
666
+ * not run; a genuine collapse is, and it does. */
667
+ react_1.default.useEffect(function () {
668
+ if (!isExpanded) {
669
+ openedByAuthor.current = false;
670
+ }
671
+ }, [isExpanded]);
672
+ /* The instruction the row gives its editor when it opens into a picker.
673
+ *
674
+ * State, not a call on the control: reqore's popover binds its own click
675
+ * listener in an effect keyed on the trigger element arriving, so a
676
+ * synthetic click dispatched from the row's own effect lands before
677
+ * anything is listening and does nothing at all. The editor opening itself
678
+ * from a prop has no such ordering to lose. */
679
+ var _w = react_1.default.useState(false), openThePicker = _w[0], setOpenThePicker = _w[1];
680
+ react_1.default.useEffect(function () {
681
+ // One shot: true for the commit the editor acts on, false again after.
682
+ if (openThePicker) {
683
+ setOpenThePicker(false);
684
+ }
685
+ }, [openThePicker]);
686
+ // Put the caret in the row's editor when the row opens, so the reader can
687
+ // answer straight away (the prototype's tap-to-edit feel) — and so
688
+ // `autoFocusFirstRequired` means what it says.
689
+ //
690
+ // Whatever control the row OFFERS: `findRowFocusTarget` knows a pick-one is
691
+ // a set of `[tabindex]` checkboxes and a selector is a `<button>`, which a
692
+ // text-only scan skipped — so the engine opened the right row and the caret
693
+ // never moved. That is most of the first questions a form asks.
694
+ //
695
+ // The row is revealed before the caret lands in it, `block: 'nearest'` on
696
+ // the ROW. A caret nobody can see is not a caret: a form opened by a click
697
+ // usually mounts below the fold, and focusing something 700px down changes
698
+ // nothing on screen while the next keystroke goes somewhere invisible.
699
+ // `nearest` is by spec a no-op for a row already in view, so a form on
700
+ // screen never jumps, and it is the row that carries the label saying what
701
+ // the question is. A caller that wants the row opened WITHOUT the caret
702
+ // has `expandFirstRequired` for exactly that.
703
+ //
704
+ // A MutationObserver, not a timer: a host editor that resolves a catalogue
705
+ // before it can draw a control mounts it several commits after this effect
706
+ // runs, and there is no fixed time to wait for it — the control appearing
707
+ // IS the event. The 60ms timer this replaces missed every such editor, and
708
+ // fired too early or too late for every other one. One shot, and it gives
709
+ // up the moment the caret belongs to someone outside this row, so it can
710
+ // never pull a reader out of a control they moved into themselves.
544
711
  var editorRef = react_1.default.useRef(null);
712
+ var rowRef = react_1.default.useRef(null);
545
713
  react_1.default.useEffect(function () {
546
714
  if (!isExpanded)
547
715
  return undefined;
548
- var id = window.setTimeout(function () {
549
- var _a;
550
- var el = (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('input:not([type="hidden"]):not([disabled]), textarea, [contenteditable="true"]');
551
- // A user-driven expand scrolls the just-opened editor into view; the
552
- // programmatic `autoFocusFirstRequired` expand must NOT — otherwise an
553
- // off-screen / below-the-fold form scrolls itself into view on mount.
554
- el === null || el === void 0 ? void 0 : el.focus((autoFocusNameRef === null || autoFocusNameRef === void 0 ? void 0 : autoFocusNameRef.current) === optionName ? { preventScroll: true } : undefined);
555
- }, 60);
556
- return function () { return window.clearTimeout(id); };
557
- }, [isExpanded, optionName, autoFocusNameRef]);
716
+ var editor = editorRef.current;
717
+ if (!editor)
718
+ return undefined;
719
+ var settled = false;
720
+ var takeCaret = function () {
721
+ var _a, _b, _c;
722
+ if (settled)
723
+ return true;
724
+ var active = document.activeElement;
725
+ if (active && active !== document.body && !((_a = rowRef.current) === null || _a === void 0 ? void 0 : _a.contains(active))) {
726
+ // Someone else holds the caret — a field the reader moved into while
727
+ // this row's editor was still arriving. Leave it where it is.
728
+ settled = true;
729
+ return true;
730
+ }
731
+ var control = (0, rowFocus_1.findRowFocusTarget)(editor);
732
+ if (!control)
733
+ return false;
734
+ // Optional call: jsdom does not implement `scrollIntoView`.
735
+ (_c = (_b = rowRef.current) === null || _b === void 0 ? void 0 : _b.scrollIntoView) === null || _c === void 0 ? void 0 : _c.call(_b, { block: 'nearest', inline: 'nearest' });
736
+ // `preventScroll`, because the reveal above has already put the row
737
+ // where it belongs; letting focus scroll as well moves it twice.
738
+ control.focus({ preventScroll: true });
739
+ /* …and if the control the row offers is a PICKER, open it.
740
+ *
741
+ * A closed picker prints the value it holds, which is exactly what the
742
+ * read row printed — so the row opened, nothing on screen changed, and
743
+ * changing the value still cost a second click. Read-first is the
744
+ * COLLAPSED row's job; an open row is the editor.
745
+ *
746
+ * Narrow on purpose, and each term is a case where read-first is still
747
+ * right: only a row the author opened themselves (a form that opens a
748
+ * row to SHOW it must not throw a list over it), only an inline scalar
749
+ * row (a complex field opens a card that is a form in its own right —
750
+ * its first control is one of several and nobody asked for it), and
751
+ * only a control that declares itself a picker, so nothing else is
752
+ * ever clicked on the author's behalf. */
753
+ if (openedByAuthor.current && inlineEditable && (0, rowFocus_1.isClosedPickerTrigger)(control)) {
754
+ openedByAuthor.current = false;
755
+ setOpenThePicker(true);
756
+ }
757
+ settled = true;
758
+ return true;
759
+ };
760
+ if (takeCaret() || typeof MutationObserver === 'undefined')
761
+ return undefined;
762
+ var observer = new MutationObserver(function () {
763
+ if (takeCaret())
764
+ observer.disconnect();
765
+ });
766
+ observer.observe(editor, { childList: true, subtree: true });
767
+ return function () { return observer.disconnect(); };
768
+ }, [isExpanded, optionName, inlineEditable]);
558
769
  // What the field held when it was opened — the baseline Cancel restores.
559
770
  //
560
771
  // Deliberately NOT `originalValue`, which is the whole form's load-time
@@ -604,7 +815,7 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
604
815
  // below an early return, so a hook placed there would run conditionally.
605
816
  var rawArgSchema = schema === null || schema === void 0 ? void 0 : schema.arg_schema;
606
817
  var argSchemaId = typeof rawArgSchema === 'string' ? rawArgSchema : undefined;
607
- var _t = react_1.default.useState(undefined), resolvedArgSchema = _t[0], setResolvedArgSchema = _t[1];
818
+ var _x = react_1.default.useState(undefined), resolvedArgSchema = _x[0], setResolvedArgSchema = _x[1];
608
819
  // Only worth a request when there is a described value to preview: a row whose
609
820
  // value is unset or scalar renders no inset, so fetching its schema would buy
610
821
  // a round trip per row and show nothing. `query` caches GETs, so rows sharing
@@ -786,7 +997,22 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
786
997
  // stacks children flush, and the class rule that used to carry a 4px gap
787
998
  // never reached this element at all — which is why two messages read as one
788
999
  // two-tone block with no space between them.
789
- var renderInfoStrip = function (m, index) { return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreMessage, { size: 'small', opaque: false, flat: true, intent: m.intent, title: m.title, style: { marginBottom: 8 }, children: m.content }, "".concat(m.content, "-").concat(index))); };
1000
+ /* A message is PROSE, so it is drawn by the host's prose renderer.
1001
+
1002
+ `markdownRendererContext` says this package renders markdown "in exactly
1003
+ one place — a field's description". That was one place short: a schema
1004
+ message is written by the same author, in the same dialect, and it landed
1005
+ here as a raw string. A Qorus warning reading "Give that step a **Fixture
1006
+ Output**" showed the asterisks, and every reference inside it stayed a raw
1007
+ `$.` path where the editor two lines above renders it as a named chip.
1008
+
1009
+ `compact` for the same reason the row inset asks for it: this is a strip
1010
+ inside a row, never a page, and markdown authored as a document opens with
1011
+ a heading that would outgrow the field label above it. Without a host
1012
+ renderer the string is drawn as it always was. */
1013
+ var renderInfoStrip = function (m, index) { return ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreMessage, { size: 'small', opaque: false, flat: true, intent: m.intent, title: m.title, style: { marginBottom: 8 }, children: markdownRenderer && typeof m.content === 'string' ?
1014
+ markdownRenderer({ value: m.content, compact: true })
1015
+ : m.content }, "".concat(m.content, "-").concat(index))); };
790
1016
  var reasonColor = function (intent) {
791
1017
  var _a;
792
1018
  return intent === 'danger' ? cDanger
@@ -817,10 +1043,12 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
817
1043
  onMouseLeave: function () { return setHighlightedOptions([]); },
818
1044
  }
819
1045
  : {};
1046
+ // Items the EDITOR inside this row published — see `rowMenuContext`.
1047
+ var _y = (0, rowMenuContext_1.useRowMenuRegistry)(), rowMenu = _y.rowMenu, editorMenuItems = _y.items;
820
1048
  // Secondary edit actions tuck into a "More" (⋮) menu so the card header stays
821
1049
  // calm: Fullscreen always, plus Remove field for a removable option. Rendered
822
1050
  // just before the Done ✓ in both expanded layouts.
823
- var moreMenu = ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreDropdown, { className: 'options-readfirst-more', icon: 'More2Fill', flat: true, minimal: true, fixed: true, size: 'small', tooltip: 'More actions', items: __spreadArray(__spreadArray([
1051
+ var moreMenu = ((0, jsx_runtime_1.jsx)(reqore_1.ReqoreDropdown, { className: 'options-readfirst-more', icon: 'More2Fill', flat: true, minimal: true, fixed: true, size: 'small', tooltip: 'More actions', items: __spreadArray(__spreadArray(__spreadArray([
824
1052
  {
825
1053
  label: 'Edit fullscreen',
826
1054
  icon: 'FullscreenLine',
@@ -840,7 +1068,7 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
840
1068
  },
841
1069
  },
842
1070
  ]
843
- : []), true), injectedOptionActionMenuItems, true) }));
1071
+ : []), true), injectedOptionActionMenuItems, true), editorMenuItems, true) }));
844
1072
  // The absorbed siblings' controls, rendered inside this row.
845
1073
  //
846
1074
  // Each keeps its own label so it is still identifiable — it is a different
@@ -888,7 +1116,7 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
888
1116
  if (isExpanded && !readOnly) {
889
1117
  if (inlineEditable) {
890
1118
  var collapse_1 = function () { return toggleExpandedOption(optionName); };
891
- var editingRow = ((0, jsx_runtime_1.jsxs)("div", __assign({ "data-field": optionName, className: 'readfirst-row readfirst-row-editing options-readfirst-inline options-readfirst-value', style: readRowHeights.current[optionName] ?
1119
+ var editingRow = ((0, jsx_runtime_1.jsxs)("div", __assign({ ref: rowRef, "data-field": optionName, className: 'readfirst-row readfirst-row-editing options-readfirst-inline options-readfirst-value', style: readRowHeights.current[optionName] ?
892
1120
  { minHeight: readRowHeights.current[optionName] }
893
1121
  : undefined }, clusterHoverProps, { children: [(0, jsx_runtime_1.jsxs)(compactRowStyles_1.StyledLabelBlock, { className: 'options-readfirst-label-block', children: [(0, jsx_runtime_1.jsxs)(compactRowStyles_1.StyledRowLabel, { role: 'button', tabIndex: 0, "aria-label": "Collapse ".concat(label), title: (schema === null || schema === void 0 ? void 0 : schema.short_desc) || undefined, "$color": cKey, "$pointer": true, onClick: collapse_1, onKeyDown: function (event) {
894
1122
  if (event.key === 'Enter' || event.key === ' ') {
@@ -910,7 +1138,7 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
910
1138
  }
911
1139
  }, children: [panelMessages.length ?
912
1140
  (0, jsx_runtime_1.jsx)(reqore_1.ReqoreControlGroup, { className: 'options-readfirst-info-panel', vertical: true, fluid: true, gapSize: 'normal', children: panelMessages.map(renderInfoStrip) })
913
- : null, absorbedNodes, renderOption(optionName, optionField, 'small', true)] }), (0, jsx_runtime_1.jsxs)(compactRowStyles_1.StyledRowActions, { children: [draftChip, editRowRevertButton, cancelEditButton, clearValueButton, inlineOptionActions.map(function (action, index) {
1141
+ : null, absorbedNodes, (0, jsx_runtime_1.jsx)(rowOpenPicker_1.RowOpenPickerContext.Provider, { value: openThePicker, children: (0, jsx_runtime_1.jsx)(rowMenuContext_1.RowMenuContext.Provider, { value: rowMenu, children: renderOption(optionName, optionField, 'small', true) }) })] }), (0, jsx_runtime_1.jsxs)(compactRowStyles_1.StyledRowActions, { children: [draftChip, editRowRevertButton, cancelEditButton, clearValueButton, inlineOptionActions.map(function (action, index) {
914
1142
  return renderInjectedOptionAction(action, index);
915
1143
  }), moreMenu, closeExpandedFieldButton] }), clusterNode] }), optionName));
916
1144
  // ALWAYS wrap the editing row in the same StyledColumn so its parent stays
@@ -968,7 +1196,7 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
968
1196
  }
969
1197
  }, children: (0, jsx_runtime_1.jsxs)(FocusedEditing_1.FocusedEditing, { isFullscreen: focusedEditing === optionName, onClose: function () { return setFocusedEditing(undefined); }, description: (schema === null || schema === void 0 ? void 0 : schema.display_name) || optionName, children: [focusedEditing === optionName ?
970
1198
  (0, jsx_runtime_1.jsx)(Description_1.Description, { longDescription: schema === null || schema === void 0 ? void 0 : schema.desc, shortDescription: schema === null || schema === void 0 ? void 0 : schema.short_desc, longDescriptionShownByDefault: true })
971
- : null, renderOption(optionName, optionField)] }) })] }, optionName));
1199
+ : null, (0, jsx_runtime_1.jsx)(rowMenuContext_1.RowMenuContext.Provider, { value: rowMenu, children: renderOption(optionName, optionField) })] }) })] }, optionName));
972
1200
  }
973
1201
  var formatted = (0, readFirst_1.formatOptionValue)(optionField, schema);
974
1202
  var empty = formatted === '';
@@ -999,9 +1227,19 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
999
1227
  : []), true);
1000
1228
  // A hash row reveals its sub-fields as read-only sub-rows under a "view
1001
1229
  // more" disclosure; the row itself still expands the real editor on click.
1230
+ /* An EXPRESSION is not a hash, whatever shape it is stored in.
1231
+
1232
+ `{is_expression: true, value: {exp, args}}` is hash-shaped, so a field
1233
+ holding one earned the structured inset and the row printed the syntax
1234
+ tree — `is_expression true / value / exp + / args 1 2` — directly under a
1235
+ summary line that already read `1 + 2`. The AST is how an expression is
1236
+ STORED, not what it is; the row's line is the whole value, and the
1237
+ expression editor is where its parts are looked at. Same exclusion, and
1238
+ for the same reason, as `schema-definition` beside it. */
1002
1239
  var hashEntries = (!hidden &&
1003
1240
  (valueType === 'hash' || valueType === 'free-hash') &&
1004
- (schema === null || schema === void 0 ? void 0 : schema.ui_type) !== 'schema-definition') ?
1241
+ (schema === null || schema === void 0 ? void 0 : schema.ui_type) !== 'schema-definition' &&
1242
+ (0, readFirst_1.getExpressionAst)(optionField) === undefined) ?
1005
1243
  (0, readFirst_1.getHashEntries)(optionField, schema)
1006
1244
  : [];
1007
1245
  // A LIST OF HASHES/objects gets the same expandable structured preview a hash
@@ -1043,6 +1281,26 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
1043
1281
  valueType === 'richtext' &&
1044
1282
  Array.isArray(optionField === null || optionField === void 0 ? void 0 : optionField.value) &&
1045
1283
  (0, common_1.richtextToSegments)(optionField.value).some(function (segment) { return segment.kind === 'tag'; });
1284
+ /* The same pair, for a value that IS one template rather than prose with
1285
+ templates embedded in it. `showsTemplateChips` above closed the richtext
1286
+ case and left this one open: a chosen reference renders as
1287
+ `ReadOnlyTemplateTag`, which carries its own popover, while the row went
1288
+ on setting a native `title` holding the raw reference. Hovering fired
1289
+ both — two tooltips in two places, and the one that says something useful
1290
+ is not the one a screenshot captures. */
1291
+ var showsTemplateTag = !hidden &&
1292
+ typeof (optionField === null || optionField === void 0 ? void 0 : optionField.value) === 'string' &&
1293
+ !(optionField === null || optionField === void 0 ? void 0 : optionField.is_expression) &&
1294
+ ((0, templates_1.isValueTemplate)(optionField.value) ||
1295
+ !!(0, templates_1.findTemplate)((_l = (_k = schema === null || schema === void 0 ? void 0 : schema.templates) !== null && _k !== void 0 ? _k : templates) !== null && _l !== void 0 ? _l : {}, optionField.value));
1296
+ /* And once more for a value that only MENTIONS references — a predicate, a
1297
+ greeting with a name in it. The row draws those as prose plus chips too,
1298
+ so a native `title` would hover the raw spelling of exactly what the chips
1299
+ replaced, and each chip's own popover would fire alongside it. */
1300
+ var showsTemplateText = !hidden &&
1301
+ !showsTemplateTag &&
1302
+ !!formatted &&
1303
+ (0, templateText_1.hasTemplateText)(formatted, (_m = schema === null || schema === void 0 ? void 0 : schema.templates) !== null && _m !== void 0 ? _m : templates, schema === null || schema === void 0 ? void 0 : schema.templateToken);
1046
1304
  // Markdown reads the same way for the same reason: the row itself can only
1047
1305
  // show a line of text, and for markdown that line is the SOURCE — the reader
1048
1306
  // gets `## ` and `**` where the point of the value is what it looks like
@@ -1134,11 +1392,17 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
1134
1392
  return {
1135
1393
  name: depName,
1136
1394
  exists: !!(options === null || options === void 0 ? void 0 : options[depName]),
1137
- label: !op ? depLabel : "".concat(depLabel, " ").concat(op === '=' ? '=' : '≠', " ").concat(expected),
1138
- fulfilled: !op ? !(0, readFirst_1.isOptionValueEmpty)(depValue)
1139
- : depValue == null ? false
1140
- : op === '=' ? String(depValue) === expected
1141
- : String(depValue) !== expected,
1395
+ // `!name` compares against nothing — it is satisfied by the sibling
1396
+ // being unanswered — so it says so, rather than going through the
1397
+ // comparison template and reading `Name ≠ undefined`.
1398
+ label: op === '!' ? "".concat(depLabel, " unset")
1399
+ : !op ? depLabel
1400
+ : "".concat(depLabel, " ").concat(op === '=' ? '=' : '≠', " ").concat(expected),
1401
+ fulfilled: op === '!' ? (0, readFirst_1.isOptionValueEmpty)(depValue)
1402
+ : !op ? !(0, readFirst_1.isOptionValueEmpty)(depValue)
1403
+ : depValue == null ? false
1404
+ : op === '=' ? String(depValue) === expected
1405
+ : String(depValue) !== expected,
1142
1406
  };
1143
1407
  };
1144
1408
  var depHighlightNames = (0, flatten_1.default)(dependencyEntries)
@@ -1172,14 +1436,26 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
1172
1436
  // with `readOnly` handed down (an id in a greyed-out input, a choice
1173
1437
  // that could still be changed); then into a read view of the same
1174
1438
  // value — which showed nothing the row could not show itself once it
1175
- // stopped truncating. So it shows everything, and opens nothing.
1439
+ // stopped truncating. So it shows everything, and opens nothing HERE.
1440
+ //
1441
+ // Where the consumer says the field is editable somewhere else, the row
1442
+ // is the way in to that place: a reader clicking a value they want to
1443
+ // change has said exactly what they want, and answering with silence
1444
+ // teaches them the surface is broken.
1176
1445
  if (fieldDisabled || readOnly) {
1446
+ if (readOnly && !fieldDisabled) {
1447
+ onReadOnlyActivate === null || onReadOnlyActivate === void 0 ? void 0 : onReadOnlyActivate(optionName);
1448
+ }
1177
1449
  return;
1178
1450
  }
1179
1451
  var target = event === null || event === void 0 ? void 0 : event.currentTarget;
1180
1452
  if ((_a = target === null || target === void 0 ? void 0 : target.classList) === null || _a === void 0 ? void 0 : _a.contains('readfirst-row')) {
1181
1453
  readRowHeights.current[optionName] = Math.round(target.getBoundingClientRect().height);
1182
1454
  }
1455
+ // The author asked for this one — so the row opens all the way (a picker
1456
+ // opens its list rather than reprinting the value as a closed trigger).
1457
+ // Only when it is OPENING: the same handler collapses an open row.
1458
+ openedByAuthor.current = !isExpanded;
1183
1459
  // Optional fields aren't "added" — every one is editable in place. Opening a
1184
1460
  // not-yet-set field just expands its editor (with an empty value); it joins
1185
1461
  // the form's output the moment a value is set (and moves to Set / Needs
@@ -1193,7 +1469,7 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
1193
1469
  (0, jsx_runtime_1.jsx)(reqore_1.ReqoreIcon, { icon: schema === null || schema === void 0 ? void 0 : schema.icon, image: schema === null || schema === void 0 ? void 0 : schema.image, size: '14px', className: 'options-readfirst-row-icon' })
1194
1470
  : null;
1195
1471
  var rowSchemaIntentColor = (schema === null || schema === void 0 ? void 0 : schema.intent) ?
1196
- (_k = theme === null || theme === void 0 ? void 0 : theme.intents) === null || _k === void 0 ? void 0 : _k[schema.intent]
1472
+ (_o = theme === null || theme === void 0 ? void 0 : theme.intents) === null || _o === void 0 ? void 0 : _o[schema.intent]
1197
1473
  : undefined;
1198
1474
  var rowStripeColor = (showStripe ? intentColor : undefined) || rowSchemaIntentColor;
1199
1475
  // Intent stripe on the block ROOT so it spans the field's whole territory:
@@ -1208,7 +1484,7 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
1208
1484
  // boxes — so the dot and the box can never disagree). Hidden (not-yet-added)
1209
1485
  // and dependency-locked rows show no dot (the add/lock affordance speaks for
1210
1486
  // them). worstIntent already folds in schema + validation messages.
1211
- var cSuccess = (_l = theme === null || theme === void 0 ? void 0 : theme.intents) === null || _l === void 0 ? void 0 : _l.success;
1487
+ var cSuccess = (_p = theme === null || theme === void 0 ? void 0 : theme.intents) === null || _p === void 0 ? void 0 : _p.success;
1212
1488
  var rowStatus = (0, readFirst_1.getReadFirstStatus)({
1213
1489
  empty: empty,
1214
1490
  required: required,
@@ -1223,7 +1499,15 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
1223
1499
  : dotStatus === 'todo' ? cWarning
1224
1500
  : dotStatus === 'set' ? cSuccess || cInfo
1225
1501
  : undefined;
1226
- var row = ((0, jsx_runtime_1.jsxs)("div", __assign({ "data-field": optionName, role: readOnly ? undefined : 'button', tabIndex: readOnly ? undefined : 0, "aria-label": readOnly ? undefined : label, className: "readfirst-row options-readfirst-value".concat(readOnly ? ' readfirst-row-read' : '').concat(hidden ? ' readfirst-row-hidden' : '').concat(fieldDisabled ? ' readfirst-row-disabled' : '').concat(isHighlighted ? ' readfirst-row-group-highlight' : '').concat(isFlashed ? ' readfirst-row-flash' : '').concat(showLabelDesc ? ' readfirst-row-info-open' : '').concat(panelMessages.length || showStructuredPreview || showCodePreview || showMarkdownPreview || showLongTextInset ? ' readfirst-row-tall' : '').concat(clusterBlockClass ? ' ' + clusterBlockClass : ''), "aria-disabled": fieldDisabled || undefined, style: stripeStyle, onClick: readOnly ? undefined : activate, onKeyDown: readOnly ? undefined : (function (event) {
1502
+ /* A read-only row is a control only when the consumer gave it somewhere to
1503
+ go. Then it takes a button's role, focus and keyboard handling, because
1504
+ a thing that acts on a click has to be reachable without a mouse and has
1505
+ to announce what it does — and "Edit X", not "X", is what it does. */
1506
+ var readOnlyIsAWayIn = !!readOnly && !fieldDisabled && !!onReadOnlyActivate;
1507
+ var actsOnClick = !readOnly || readOnlyIsAWayIn;
1508
+ var row = ((0, jsx_runtime_1.jsxs)("div", __assign({ "data-field": optionName, role: actsOnClick ? 'button' : undefined, tabIndex: actsOnClick ? 0 : undefined, "aria-label": readOnlyIsAWayIn ? "Edit ".concat(label)
1509
+ : readOnly ? undefined
1510
+ : label, className: "readfirst-row options-readfirst-value".concat(readOnly ? ' readfirst-row-read' : '').concat(readOnlyIsAWayIn ? ' readfirst-row-way-in' : '').concat(hidden ? ' readfirst-row-hidden' : '').concat(fieldDisabled ? ' readfirst-row-disabled' : '').concat(isHighlighted ? ' readfirst-row-group-highlight' : '').concat(isFlashed ? ' readfirst-row-flash' : '').concat(showLabelDesc ? ' readfirst-row-info-open' : '').concat(panelMessages.length || showStructuredPreview || showCodePreview || showMarkdownPreview || showLongTextInset ? ' readfirst-row-tall' : '').concat(clusterBlockClass ? ' ' + clusterBlockClass : ''), "aria-disabled": fieldDisabled || undefined, style: readOnlyIsAWayIn ? __assign(__assign({}, stripeStyle), { cursor: 'pointer' }) : stripeStyle, onClick: actsOnClick ? activate : undefined, onKeyDown: !actsOnClick ? undefined : (function (event) {
1227
1511
  if (event.key === 'Enter' || event.key === ' ') {
1228
1512
  event.preventDefault();
1229
1513
  activate(event);
@@ -1259,6 +1543,8 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
1259
1543
  !showCodePreview &&
1260
1544
  !showMarkdownPreview &&
1261
1545
  !showsTemplateChips &&
1546
+ !showsTemplateTag &&
1547
+ !showsTemplateText &&
1262
1548
  typeof formatted === 'string') ?
1263
1549
  formatted
1264
1550
  : undefined, "$color": empty || hidden ? "".concat(cMuted, "66") : cKey, "$empty": empty || hidden, children: [(showMarkdownPreview || showLongTextInset || (previewWithSchema && showStructuredPreview)) ?
@@ -1311,7 +1597,7 @@ exports.CompactRow = (0, react_1.memo)(function (_a) {
1311
1597
  // consumer that registers nothing — the heading is
1312
1598
  // accented either way, just in the theme's own colour
1313
1599
  // rather than one this library picked.
1314
- accent: ((_m = theme === null || theme === void 0 ? void 0 : theme.intents) === null || _m === void 0 ? void 0 : _m.custom1) || cInfo,
1600
+ accent: ((_q = theme === null || theme === void 0 ? void 0 : theme.intents) === null || _q === void 0 ? void 0 : _q.custom1) || cInfo,
1315
1601
  }, onItemClick: function () { return activate(); } })
1316
1602
  : (0, jsx_runtime_1.jsx)(StructuredDataView_1.StructuredDataView, { value: optionField === null || optionField === void 0 ? void 0 : optionField.value, collapsibleRoot: false, showTypes: showFieldTypes, defaultExpandDepth: 2, onItemClick: function () { return activate(); } }) }) }) })
1317
1603
  : null, showCodePreview ?