@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.
- package/.claude/CLAUDE.md +25 -0
- package/.tasks/INDEX.md +1 -0
- package/.tasks/STORY_NETWORK_ISOLATION.md +98 -0
- package/README.md +1 -1
- package/design/FORM_ENGINE_COMPACT.md +34 -0
- package/design/FORM_ENGINE_EXPRESSIONS.md +110 -0
- package/design/FORM_ENGINE_FIELD_MIGRATION.md +155 -1
- package/design/IDE_INTEGRATION.md +13 -0
- package/design/RENDER_EXPRESSION_TRANSPORT.md +76 -10
- package/design/SELECT_DIALOG_FLOOR.md +86 -0
- package/design/SMART_EDITOR_UX.md +21 -0
- package/design/UNAVAILABLE_ALLOWED_VALUES.md +226 -0
- package/dist/components/composer/Composer.d.ts +8 -3
- package/dist/components/composer/Composer.d.ts.map +1 -1
- package/dist/components/composer/Composer.js.map +1 -1
- package/dist/components/dpqlEditor/DpqlEditor.d.ts.map +1 -1
- package/dist/components/dpqlEditor/DpqlEditor.js +3 -3
- package/dist/components/dpqlEditor/DpqlEditor.js.map +1 -1
- package/dist/components/dpqlEditor/dpqlHelpers.d.ts +6 -0
- package/dist/components/dpqlEditor/dpqlHelpers.d.ts.map +1 -1
- package/dist/components/dpqlEditor/dpqlHelpers.js +21 -2
- package/dist/components/dpqlEditor/dpqlHelpers.js.map +1 -1
- package/dist/components/dpqlEditor/dpqlProbe.d.ts +37 -0
- package/dist/components/dpqlEditor/dpqlProbe.d.ts.map +1 -0
- package/dist/components/dpqlEditor/dpqlProbe.js +203 -0
- package/dist/components/dpqlEditor/dpqlProbe.js.map +1 -0
- package/dist/components/dpqlEditor/dpqlTags.d.ts +8 -0
- package/dist/components/dpqlEditor/dpqlTags.d.ts.map +1 -1
- package/dist/components/dpqlEditor/dpqlTags.js +5 -1
- package/dist/components/dpqlEditor/dpqlTags.js.map +1 -1
- package/dist/components/dpqlEditor/index.d.ts +4 -0
- package/dist/components/dpqlEditor/index.d.ts.map +1 -1
- package/dist/components/dpqlEditor/index.js +5 -1
- package/dist/components/dpqlEditor/index.js.map +1 -1
- package/dist/components/dpqlEditor/types.d.ts +42 -2
- package/dist/components/dpqlEditor/types.d.ts.map +1 -1
- package/dist/components/dpqlEditor/types.js +0 -4
- package/dist/components/dpqlEditor/types.js.map +1 -1
- package/dist/components/dpqlEditor/useDpqlProbe.d.ts +4 -0
- package/dist/components/dpqlEditor/useDpqlProbe.d.ts.map +1 -0
- package/dist/components/dpqlEditor/useDpqlProbe.js +29 -0
- package/dist/components/dpqlEditor/useDpqlProbe.js.map +1 -0
- package/dist/components/dpqlEditor/useDpqlSession.d.ts +5 -2
- package/dist/components/dpqlEditor/useDpqlSession.d.ts.map +1 -1
- package/dist/components/dpqlEditor/useDpqlSession.js +8 -2
- package/dist/components/dpqlEditor/useDpqlSession.js.map +1 -1
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +414 -128
- package/dist/components/form/engine/CompactRow.js.map +1 -1
- package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
- package/dist/components/form/engine/CompactToolbar.js +5 -2
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +273 -22
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +669 -192
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/FormFieldsSkeleton.d.ts +48 -0
- package/dist/components/form/engine/FormFieldsSkeleton.d.ts.map +1 -0
- package/dist/components/form/engine/FormFieldsSkeleton.js +41 -0
- package/dist/components/form/engine/FormFieldsSkeleton.js.map +1 -0
- package/dist/components/form/engine/OptionFieldMessages.d.ts +108 -2
- package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.js +203 -13
- package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.d.ts.map +1 -1
- package/dist/components/form/engine/_structuredData/SchemaDataView.js +68 -5
- package/dist/components/form/engine/_structuredData/SchemaDataView.js.map +1 -1
- package/dist/components/form/engine/compactRowContext.d.ts +15 -1
- package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowContext.js.map +1 -1
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +55 -29
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/optionActions.d.ts +37 -1
- package/dist/components/form/engine/optionActions.d.ts.map +1 -1
- package/dist/components/form/engine/optionActions.js +71 -1
- package/dist/components/form/engine/optionActions.js.map +1 -1
- package/dist/components/form/engine/optionsContext.d.ts +22 -0
- package/dist/components/form/engine/optionsContext.d.ts.map +1 -0
- package/dist/components/form/engine/optionsContext.js +20 -0
- package/dist/components/form/engine/optionsContext.js.map +1 -0
- package/dist/components/form/engine/readFirst.d.ts +42 -1
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +76 -8
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rendererTypes.d.ts +42 -3
- package/dist/components/form/engine/rendererTypes.d.ts.map +1 -1
- package/dist/components/form/engine/rendererTypes.js +3 -1
- package/dist/components/form/engine/rendererTypes.js.map +1 -1
- package/dist/components/form/engine/rowFocus.d.ts +30 -0
- package/dist/components/form/engine/rowFocus.d.ts.map +1 -0
- package/dist/components/form/engine/rowFocus.js +93 -0
- package/dist/components/form/engine/rowFocus.js.map +1 -0
- package/dist/components/form/engine/rowMenuContext.d.ts +78 -0
- package/dist/components/form/engine/rowMenuContext.d.ts.map +1 -0
- package/dist/components/form/engine/rowMenuContext.js +103 -0
- package/dist/components/form/engine/rowMenuContext.js.map +1 -0
- package/dist/components/form/engine/rowOpenPicker.d.ts +27 -0
- package/dist/components/form/engine/rowOpenPicker.d.ts.map +1 -0
- package/dist/components/form/engine/rowOpenPicker.js +30 -0
- package/dist/components/form/engine/rowOpenPicker.js.map +1 -0
- package/dist/components/form/engine/typeChoices.d.ts +42 -0
- package/dist/components/form/engine/typeChoices.d.ts.map +1 -0
- package/dist/components/form/engine/typeChoices.js +33 -0
- package/dist/components/form/engine/typeChoices.js.map +1 -0
- package/dist/components/form/expressions/DpqlRendering.d.ts +26 -0
- package/dist/components/form/expressions/DpqlRendering.d.ts.map +1 -0
- package/dist/components/form/expressions/DpqlRendering.js +53 -0
- package/dist/components/form/expressions/DpqlRendering.js.map +1 -0
- package/dist/components/form/expressions/ExpressionField.d.ts +7 -1
- package/dist/components/form/expressions/ExpressionField.d.ts.map +1 -1
- package/dist/components/form/expressions/ExpressionField.js +203 -45
- package/dist/components/form/expressions/ExpressionField.js.map +1 -1
- package/dist/components/form/expressions/argumentPresence.d.ts +84 -0
- package/dist/components/form/expressions/argumentPresence.d.ts.map +1 -0
- package/dist/components/form/expressions/argumentPresence.js +217 -0
- package/dist/components/form/expressions/argumentPresence.js.map +1 -0
- package/dist/components/form/expressions/builder/argumentLabel.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/argumentLabel.js +9 -4
- package/dist/components/form/expressions/builder/argumentLabel.js.map +1 -1
- package/dist/components/form/expressions/builder/index.d.ts +15 -2
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +21 -65
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/builder/renderTemplate.d.ts +4 -1
- package/dist/components/form/expressions/builder/renderTemplate.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/renderTemplate.js +10 -67
- package/dist/components/form/expressions/builder/renderTemplate.js.map +1 -1
- package/dist/components/form/expressions/mockExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/mockExpressions.js +8 -4
- package/dist/components/form/expressions/mockExpressions.js.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts +1 -1
- package/dist/components/form/expressions/renderExpressionToText.d.ts.map +1 -1
- package/dist/components/form/expressions/renderExpressionToText.js +95 -19
- package/dist/components/form/expressions/renderExpressionToText.js.map +1 -1
- package/dist/components/form/expressions/types.d.ts +17 -2
- package/dist/components/form/expressions/types.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.js +5 -2
- package/dist/components/form/expressions/useExpressions.js.map +1 -1
- package/dist/components/form/expressions/useRenderExpression.d.ts +7 -16
- package/dist/components/form/expressions/useRenderExpression.d.ts.map +1 -1
- package/dist/components/form/expressions/useRenderExpression.js +50 -126
- package/dist/components/form/expressions/useRenderExpression.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts +21 -0
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +54 -8
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +11 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +52 -8
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts +7 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +91 -28
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/date/Date.d.ts +1 -1
- package/dist/components/form/fields/date/Date.d.ts.map +1 -1
- package/dist/components/form/fields/date/Date.js +16 -2
- package/dist/components/form/fields/date/Date.js.map +1 -1
- package/dist/components/form/fields/long-string/LongString.d.ts.map +1 -1
- package/dist/components/form/fields/long-string/LongString.js +14 -2
- package/dist/components/form/fields/long-string/LongString.js.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +50 -4
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/number/Number.d.ts +12 -1
- package/dist/components/form/fields/number/Number.d.ts.map +1 -1
- package/dist/components/form/fields/number/Number.js +6 -3
- package/dist/components/form/fields/number/Number.js.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.d.ts +47 -2
- package/dist/components/form/fields/rich-text/RichText.d.ts.map +1 -1
- package/dist/components/form/fields/rich-text/RichText.js +153 -12
- package/dist/components/form/fields/rich-text/RichText.js.map +1 -1
- package/dist/components/form/fields/select/Select.d.ts.map +1 -1
- package/dist/components/form/fields/select/Select.js +120 -20
- package/dist/components/form/fields/select/Select.js.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.d.ts +166 -9
- package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.js +200 -7
- package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts +2 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.d.ts.map +1 -1
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js +3 -2
- package/dist/components/form/fields/template/ReadOnlyTemplateTag.js.map +1 -1
- package/dist/components/form/fields/template/TemplateField.d.ts +34 -2
- package/dist/components/form/fields/template/TemplateField.d.ts.map +1 -1
- package/dist/components/form/fields/template/TemplateField.js +606 -58
- package/dist/components/form/fields/template/TemplateField.js.map +1 -1
- package/dist/components/form/fields/template/TemplateText.d.ts +36 -0
- package/dist/components/form/fields/template/TemplateText.d.ts.map +1 -0
- package/dist/components/form/fields/template/TemplateText.js +80 -0
- package/dist/components/form/fields/template/TemplateText.js.map +1 -0
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +4 -0
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/smartEditor/SmartEditor.d.ts.map +1 -1
- package/dist/components/smartEditor/SmartEditor.js +29 -8
- package/dist/components/smartEditor/SmartEditor.js.map +1 -1
- package/dist/components/smartEditor/useLspSession.d.ts +5 -1
- package/dist/components/smartEditor/useLspSession.d.ts.map +1 -1
- package/dist/components/smartEditor/useLspSession.js +16 -2
- package/dist/components/smartEditor/useLspSession.js.map +1 -1
- package/dist/components/supportTicket/TicketReplyBox.d.ts +8 -4
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -1
- package/dist/components/supportTicket/TicketReplyBox.js.map +1 -1
- package/dist/helpers/common.d.ts.map +1 -1
- package/dist/helpers/common.js +7 -3
- package/dist/helpers/common.js.map +1 -1
- package/dist/helpers/dpqlDetection.d.ts +58 -0
- package/dist/helpers/dpqlDetection.d.ts.map +1 -0
- package/dist/helpers/dpqlDetection.js +164 -0
- package/dist/helpers/dpqlDetection.js.map +1 -0
- package/dist/helpers/optionUiTypes.d.ts +31 -3
- package/dist/helpers/optionUiTypes.d.ts.map +1 -1
- package/dist/helpers/optionUiTypes.js +36 -1
- package/dist/helpers/optionUiTypes.js.map +1 -1
- package/dist/helpers/options.d.ts +57 -0
- package/dist/helpers/options.d.ts.map +1 -1
- package/dist/helpers/options.js +97 -1
- package/dist/helpers/options.js.map +1 -1
- package/dist/helpers/templateItems.d.ts +47 -0
- package/dist/helpers/templateItems.d.ts.map +1 -0
- package/dist/helpers/templateItems.js +88 -0
- package/dist/helpers/templateItems.js.map +1 -0
- package/dist/helpers/templateText.d.ts +80 -0
- package/dist/helpers/templateText.d.ts.map +1 -0
- package/dist/helpers/templateText.js +291 -0
- package/dist/helpers/templateText.js.map +1 -0
- package/dist/helpers/templates.d.ts +30 -12
- package/dist/helpers/templates.d.ts.map +1 -1
- package/dist/helpers/templates.js +91 -33
- package/dist/helpers/templates.js.map +1 -1
- package/dist/helpers/validations.d.ts +35 -8
- package/dist/helpers/validations.d.ts.map +1 -1
- package/dist/helpers/validations.js +162 -25
- package/dist/helpers/validations.js.map +1 -1
- package/dist/hooks/useArgSchema.d.ts +2 -1
- package/dist/hooks/useArgSchema.d.ts.map +1 -1
- package/dist/hooks/useFetch/useFetch.d.ts +3 -1
- package/dist/hooks/useFetch/useFetch.d.ts.map +1 -1
- package/dist/hooks/useFetch/useFetch.js +47 -9
- package/dist/hooks/useFetch/useFetch.js.map +1 -1
- package/dist/hooks/useTemplates.d.ts +20 -0
- package/dist/hooks/useTemplates.d.ts.map +1 -1
- package/dist/hooks/useTemplates.js +34 -2
- package/dist/hooks/useTemplates.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/fetch.d.ts +151 -1
- package/dist/utils/fetch.d.ts.map +1 -1
- package/dist/utils/fetch.js +256 -44
- package/dist/utils/fetch.js.map +1 -1
- package/dist/utils/lspClient.d.ts +6 -0
- package/dist/utils/lspClient.d.ts.map +1 -1
- package/dist/utils/lspClient.js +62 -11
- package/dist/utils/lspClient.js.map +1 -1
- package/dist/utils/websocket.d.ts +6 -0
- package/dist/utils/websocket.d.ts.map +1 -1
- package/dist/utils/websocket.js +15 -7
- package/dist/utils/websocket.js.map +1 -1
- package/package.json +10 -6
- package/src/components/composer/Composer.tsx +7 -2
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +3 -119
- package/src/components/dpqlEditor/DpqlEditor.tsx +5 -2
- package/src/components/dpqlEditor/dpqlHelpers.ts +20 -1
- package/src/components/dpqlEditor/dpqlProbe.ts +165 -0
- package/src/components/dpqlEditor/dpqlTags.ts +13 -0
- package/src/components/dpqlEditor/index.ts +4 -0
- package/src/components/dpqlEditor/types.ts +38 -2
- package/src/components/dpqlEditor/useDpqlProbe.ts +33 -0
- package/src/components/dpqlEditor/useDpqlSession.ts +20 -4
- package/src/components/form/engine/CompactRow.tsx +487 -160
- package/src/components/form/engine/CompactToolbar.tsx +10 -3
- package/src/components/form/engine/FormEngine.stories.tsx +1269 -201
- package/src/components/form/engine/FormEngine.tsx +922 -270
- package/src/components/form/engine/FormEngineStickyHeaders.stories.tsx +331 -0
- package/src/components/form/engine/FormFieldsSkeleton.tsx +77 -0
- package/src/components/form/engine/OptionFieldMessages.tsx +300 -14
- package/src/components/form/engine/_structuredData/SchemaDataView.tsx +94 -3
- package/src/components/form/engine/compactRowContext.ts +15 -6
- package/src/components/form/engine/compactRowStyles.ts +56 -2
- package/src/components/form/engine/compactToolbarContext.ts +1 -1
- package/src/components/form/engine/optionActions.ts +89 -1
- package/src/components/form/engine/optionsContext.ts +24 -0
- package/src/components/form/engine/readFirst.ts +88 -8
- package/src/components/form/engine/rendererTypes.ts +48 -4
- package/src/components/form/engine/rowFocus.ts +101 -0
- package/src/components/form/engine/rowMenuContext.ts +159 -0
- package/src/components/form/engine/rowOpenPicker.ts +27 -0
- package/src/components/form/engine/typeChoices.ts +63 -0
- package/src/components/form/expressions/DpqlRendering.tsx +85 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +826 -48
- package/src/components/form/expressions/ExpressionField.tsx +269 -55
- package/src/components/form/expressions/argumentPresence.ts +213 -0
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +38 -21
- package/src/components/form/expressions/builder/argumentLabel.tsx +10 -4
- package/src/components/form/expressions/builder/index.tsx +39 -81
- package/src/components/form/expressions/builder/renderTemplate.tsx +23 -56
- package/src/components/form/expressions/dpqlMockLanguage.ts +565 -0
- package/src/components/form/expressions/dpqlMockLsp.ts +109 -83
- package/src/components/form/expressions/mockExpressions.ts +8 -4
- package/src/components/form/expressions/renderExpressionToText.ts +112 -22
- package/src/components/form/expressions/types.ts +17 -2
- package/src/components/form/expressions/useExpressions.ts +5 -1
- package/src/components/form/expressions/useRenderExpression.ts +43 -127
- package/src/components/form/fields/Field.tsx +102 -20
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +106 -44
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +85 -18
- package/src/components/form/fields/auto/AutoFormField.tsx +97 -21
- package/src/components/form/fields/date/Date.tsx +17 -1
- package/src/components/form/fields/long-string/LongString.tsx +15 -3
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +65 -6
- package/src/components/form/fields/number/Number.tsx +25 -0
- package/src/components/form/fields/rich-text/RichText.tsx +248 -30
- package/src/components/form/fields/select/Select.stories.tsx +392 -0
- package/src/components/form/fields/select/Select.tsx +164 -27
- package/src/components/form/fields/select/SelectCollection.tsx +350 -6
- package/src/components/form/fields/template/ReadOnlyTemplateTag.tsx +4 -2
- package/src/components/form/fields/template/TemplateField.stories.tsx +93 -14
- package/src/components/form/fields/template/TemplateField.tsx +825 -131
- package/src/components/form/fields/template/TemplateText.tsx +115 -0
- package/src/components/form/index.tsx +4 -0
- package/src/components/log/Log.stories.tsx +2 -1
- package/src/components/searchFilterBar/SearchFilterBar.stories.tsx +3 -3
- package/src/components/smartEditor/SmartEditor.tsx +41 -9
- package/src/components/smartEditor/__fixtures__/mockLspServer.ts +52 -3
- package/src/components/smartEditor/useLspSession.ts +21 -3
- package/src/components/supportTicket/TicketReplyBox.tsx +8 -4
- package/src/components/ticketThread/TicketThread.stories.tsx +1 -0
- package/src/helpers/common.ts +7 -3
- package/src/helpers/dpqlDetection.ts +209 -0
- package/src/helpers/optionUiTypes.ts +38 -0
- package/src/helpers/options.ts +96 -0
- package/src/helpers/templateItems.ts +81 -0
- package/src/helpers/templateText.ts +350 -0
- package/src/helpers/templates.ts +94 -41
- package/src/helpers/validations.ts +203 -51
- package/src/hooks/useFetch/useFetch.stories.tsx +6 -5
- package/src/hooks/useFetch/useFetch.tsx +53 -7
- package/src/hooks/useStorage/useStorage.stories.tsx +2 -1
- package/src/hooks/useTemplates.ts +41 -0
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +2 -1
- package/src/index.tsx +14 -1
- package/src/stores/currentUser/currentUser.stories.tsx +2 -1
- package/src/stories/storyNetwork.ts +92 -0
- package/src/utils/fetch.ts +311 -21
- package/src/utils/lspClient.ts +68 -11
- package/src/utils/websocket.ts +20 -9
- package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts +0 -73
- package/dist/components/form/engine/__fixtures__/basicFormOptions.d.ts.map +0 -1
- package/dist/components/form/engine/__fixtures__/basicFormOptions.js +0 -317
- package/dist/components/form/engine/__fixtures__/basicFormOptions.js.map +0 -1
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts +0 -41
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.d.ts.map +0 -1
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js +0 -45
- package/dist/components/form/engine/__fixtures__/fieldChromeOptions.js.map +0 -1
- package/dist/components/form/expressions/dpqlMockLsp.d.ts +0 -3
- package/dist/components/form/expressions/dpqlMockLsp.d.ts.map +0 -1
- package/dist/components/form/expressions/dpqlMockLsp.js +0 -91
- package/dist/components/form/expressions/dpqlMockLsp.js.map +0 -1
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts +0 -2
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.d.ts.map +0 -1
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.js +0 -9
- package/dist/components/form/fields/template/__fixtures__/longExampleValue.js.map +0 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts +0 -55
- package/dist/components/smartEditor/__fixtures__/mockLspServer.d.ts.map +0 -1
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js +0 -126
- package/dist/components/smartEditor/__fixtures__/mockLspServer.js.map +0 -1
- package/dist/stories/Data/fileFieldArgSchema.json +0 -86
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Copyright 2026 Qore Technologies, s.r.o.
|
|
2
|
+
import { ReqoreTag } from '@qoretechnologies/reqore';
|
|
3
|
+
import { IReqoreFormTemplates } from '@qoretechnologies/reqore/dist/components/Textarea';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { IRichtextSegment } from '../../../../helpers/common';
|
|
6
|
+
import {
|
|
7
|
+
findTemplate,
|
|
8
|
+
getTemplateTagStyle,
|
|
9
|
+
templateTooltip,
|
|
10
|
+
TTemplateMeta,
|
|
11
|
+
} from '../../../../helpers/templates';
|
|
12
|
+
|
|
13
|
+
export interface ITemplateTextProps {
|
|
14
|
+
/** Prose runs and reference chips, from `templateTextSegments` or
|
|
15
|
+
* `richtextToSegments` — both produce the same shape on purpose. */
|
|
16
|
+
segments: IRichtextSegment[];
|
|
17
|
+
/** The catalogue the chips are named and explained from. */
|
|
18
|
+
templates?: IReqoreFormTemplates;
|
|
19
|
+
/**
|
|
20
|
+
* A read-only row is the ONLY rendering its value gets, so it wraps and shows
|
|
21
|
+
* every chip. An editable row's value line is a one-line summary you click to
|
|
22
|
+
* open, so it clips instead of growing the row.
|
|
23
|
+
*/
|
|
24
|
+
full?: boolean;
|
|
25
|
+
className?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Flattens the line breaks in one prose segment of a one-line SUMMARY.
|
|
30
|
+
*
|
|
31
|
+
* Every other value type reaches the collapsed row through
|
|
32
|
+
* `whiteSpace: 'nowrap'`, which collapses newlines for free. This renderer is
|
|
33
|
+
* the only one that opts into `'pre'` — it has to, or the space separating a
|
|
34
|
+
* word from the chip beside it is dropped — and `pre` also honours the
|
|
35
|
+
* newlines, which `nowrap` would have eaten.
|
|
36
|
+
*
|
|
37
|
+
* So a genuinely multi-line value (an alert rule's Gmail message body is five
|
|
38
|
+
* `\n`-separated lines) gave every segment after the first a blank first line.
|
|
39
|
+
* The wrapper centres its items, so a two-line-tall box centred against
|
|
40
|
+
* one-line chips put every word 12px below the chip beside it, and the row read
|
|
41
|
+
* as a staircase. Measured on supah: prose boxes 30px against 14px chips.
|
|
42
|
+
*
|
|
43
|
+
* A space, not nothing: consecutive prose segments are merged before they get
|
|
44
|
+
* here, so a value with a break and no chip between its lines would otherwise
|
|
45
|
+
* lose the word boundary entirely.
|
|
46
|
+
*/
|
|
47
|
+
const collapseSummaryBreaks = (text: string): string => text.replace(/\s*\r?\n\s*/g, ' ');
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A value that mentions template references, drawn for READING.
|
|
51
|
+
*
|
|
52
|
+
* One renderer, because a value that reads two ways depending on which surface
|
|
53
|
+
* shows it is the defect this exists to close: a skip-when predicate read
|
|
54
|
+
* `$._case.mode != 'simulate'` on the compact row while the editor it opens
|
|
55
|
+
* drew the same reference as a named chip.
|
|
56
|
+
*
|
|
57
|
+
* The literal text around a reference is the AUTHOR'S, and it survives exactly
|
|
58
|
+
* as typed — a predicate is a comparison they wrote, and only the reference in
|
|
59
|
+
* it is the thing the template mechanism exists to name.
|
|
60
|
+
*
|
|
61
|
+
* The chip is `ReqoreTag`, the same one `ReadOnlyTemplateTag` and the rich-text
|
|
62
|
+
* editor use, with the same `$`-icon, the same intent scheme
|
|
63
|
+
* (`getTemplateTagStyle`) and the same hover card (`templateTooltip`), so a
|
|
64
|
+
* reference looks identical wherever it is shown.
|
|
65
|
+
*/
|
|
66
|
+
export const TemplateText = memo(({ segments, templates, full, className }: ITemplateTextProps) => (
|
|
67
|
+
<span
|
|
68
|
+
className={`reqraft-template-text${className ? ` ${className}` : ''}`}
|
|
69
|
+
style={{
|
|
70
|
+
display: 'inline-flex',
|
|
71
|
+
alignItems: 'center',
|
|
72
|
+
gap: 4,
|
|
73
|
+
minWidth: 0,
|
|
74
|
+
maxWidth: '100%',
|
|
75
|
+
...(full ? { flexWrap: 'wrap' } : { overflow: 'hidden' }),
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
{segments.map((segment, index) =>
|
|
79
|
+
segment.kind === 'tag' ?
|
|
80
|
+
<ReqoreTag
|
|
81
|
+
key={`tag-${index}`}
|
|
82
|
+
className='reqraft-template-chip'
|
|
83
|
+
size='tiny'
|
|
84
|
+
// The chip's label has to be the same size as the prose it is embedded
|
|
85
|
+
// in. The wrapper centres boxes, so two different text sizes centred
|
|
86
|
+
// against each other cannot share a baseline and the chips visibly
|
|
87
|
+
// float above the words. Inheriting makes centring align the baselines
|
|
88
|
+
// too, at whatever font size the consuming app uses.
|
|
89
|
+
style={{ fontSize: 'inherit' }}
|
|
90
|
+
icon='ExchangeDollarLine'
|
|
91
|
+
label={segment.text || segment.value}
|
|
92
|
+
// The same card the chosen-template chip shows: what the value IS, not
|
|
93
|
+
// the reference that names it.
|
|
94
|
+
tooltip={templateTooltip(templates, segment.value || '')}
|
|
95
|
+
{...getTemplateTagStyle(
|
|
96
|
+
(templates ? findTemplate(templates, segment.value || '') : undefined)?.metadata as
|
|
97
|
+
| TTemplateMeta
|
|
98
|
+
| undefined
|
|
99
|
+
)}
|
|
100
|
+
/>
|
|
101
|
+
: <span
|
|
102
|
+
key={`text-${index}`}
|
|
103
|
+
className='reqraft-template-prose'
|
|
104
|
+
// `pre`, not the row's `nowrap`: the single space that separates a
|
|
105
|
+
// word from the chip beside it is real content here, and collapsing
|
|
106
|
+
// it would join them. A read row keeps the newlines too.
|
|
107
|
+
style={{ whiteSpace: full ? 'pre-wrap' : 'pre' }}
|
|
108
|
+
>
|
|
109
|
+
{full ? segment.text : collapseSummaryBreaks(segment.text)}
|
|
110
|
+
</span>
|
|
111
|
+
)}
|
|
112
|
+
</span>
|
|
113
|
+
));
|
|
114
|
+
|
|
115
|
+
TemplateText.displayName = 'TemplateText';
|
|
@@ -24,8 +24,12 @@ export * from './fields/string/String';
|
|
|
24
24
|
export * from './fields/template/ExampleValueModal';
|
|
25
25
|
export * from './fields/template/TemplateField';
|
|
26
26
|
export * from './engine/FormEngine';
|
|
27
|
+
export * from './engine/FormFieldsSkeleton';
|
|
27
28
|
export * from './engine/optionActions';
|
|
28
29
|
export * from './engine/rendererTypes';
|
|
30
|
+
// A host's own editor is a compact row's editor too, so the instruction a row
|
|
31
|
+
// gives when the AUTHOR opens it into a picker is part of the public surface.
|
|
32
|
+
export * from './engine/rowOpenPicker';
|
|
29
33
|
export * from './expressions/types';
|
|
30
34
|
export * from './expressions/useExpressions';
|
|
31
35
|
// Named (not wildcard) so the `_resetRenderExpressionTransportForTests`
|
|
@@ -2,6 +2,7 @@ import { StoryObj } from '@storybook/react-vite';
|
|
|
2
2
|
import { expect, fireEvent, fn } from 'storybook/test';
|
|
3
3
|
import { Server } from 'mock-socket';
|
|
4
4
|
import { sleep, testsClickButton, testsWaitForText } from '../../../__tests__/utils';
|
|
5
|
+
import { storySocketUrl } from '../../stories/storyNetwork';
|
|
5
6
|
import { StoryMeta } from '../../types';
|
|
6
7
|
import { ReqraftLog } from './Log';
|
|
7
8
|
|
|
@@ -24,7 +25,7 @@ const meta = {
|
|
|
24
25
|
mockdate: new Date('2024-01-01T08:00:00.000Z'),
|
|
25
26
|
},
|
|
26
27
|
async beforeEach() {
|
|
27
|
-
const url =
|
|
28
|
+
const url = storySocketUrl('log-test');
|
|
28
29
|
let server = new Server(url);
|
|
29
30
|
let killTimeout: NodeJS.Timeout;
|
|
30
31
|
let fakeInterval: NodeJS.Timeout;
|
|
@@ -19,9 +19,9 @@ const meta = {
|
|
|
19
19
|
icon: 'Filter3Line' as const,
|
|
20
20
|
label: 'Any status',
|
|
21
21
|
items: [
|
|
22
|
-
{ label: 'Any status',
|
|
23
|
-
{ label: 'Open',
|
|
24
|
-
{ label: 'Resolved',
|
|
22
|
+
{ label: 'Any status', selected: true, onClick: fn() },
|
|
23
|
+
{ label: 'Open', onClick: fn() },
|
|
24
|
+
{ label: 'Resolved', onClick: fn() },
|
|
25
25
|
],
|
|
26
26
|
},
|
|
27
27
|
],
|
|
@@ -18,7 +18,16 @@ import {
|
|
|
18
18
|
} from '@qoretechnologies/reqore/dist/components/RichTextEditor';
|
|
19
19
|
import { getReadableColor } from '@qoretechnologies/reqore/dist/helpers/colors';
|
|
20
20
|
import { IReqoreTooltip } from '@qoretechnologies/reqore/dist/types/global';
|
|
21
|
-
import React, {
|
|
21
|
+
import React, {
|
|
22
|
+
forwardRef,
|
|
23
|
+
memo,
|
|
24
|
+
useCallback,
|
|
25
|
+
useEffect,
|
|
26
|
+
useImperativeHandle,
|
|
27
|
+
useMemo,
|
|
28
|
+
useReducer,
|
|
29
|
+
useRef,
|
|
30
|
+
} from 'react';
|
|
22
31
|
import { Editor, NodeEntry, Range, Transforms } from 'slate';
|
|
23
32
|
import { ReactEditor, RenderLeafProps } from 'slate-react';
|
|
24
33
|
import { ILspSignatureHelp } from '../../utils/lspClient.types';
|
|
@@ -320,6 +329,14 @@ export const SmartEditor = forwardRef<TReqoreRichTextEditorRef, ISmartEditorProp
|
|
|
320
329
|
// ReqoreRichTextEditor's tokenizer mid-token (typed `@s` collapses to a
|
|
321
330
|
// tag chip, making the rest of the field name impossible to type).
|
|
322
331
|
const slateValueRef = useRef<ISlateElement[] | null>(null);
|
|
332
|
+
/* Counts the node trees the editor has reported. The tree a value converts
|
|
333
|
+
to is not always the tree the editor holds: Slate normalises a value set
|
|
334
|
+
from outside — a leading chip gains an empty text leaf before it — without
|
|
335
|
+
changing its text, so `value` never changes and the cache above kept the
|
|
336
|
+
tree the editor no longer had. Every offset computed from it (token
|
|
337
|
+
colours, diagnostics, signature help) then pointed one leaf off: a Text
|
|
338
|
+
view seeded with `"$local:name" == "John"` was never highlighted. */
|
|
339
|
+
const [editorTreeVersion, noteEditorTree] = useReducer((count: number) => count + 1, 0);
|
|
323
340
|
|
|
324
341
|
useImperativeHandle(ref, () => editorRef.current as TReqoreRichTextEditorRef, []);
|
|
325
342
|
|
|
@@ -350,7 +367,17 @@ export const SmartEditor = forwardRef<TReqoreRichTextEditorRef, ISmartEditorProp
|
|
|
350
367
|
const next = converter.toSlateNodes(value);
|
|
351
368
|
slateValueRef.current = next;
|
|
352
369
|
return next;
|
|
353
|
-
|
|
370
|
+
// `editorTreeVersion` re-reads the cache once the editor reports its own tree.
|
|
371
|
+
}, [value, converter, editorTreeVersion]);
|
|
372
|
+
|
|
373
|
+
/* A value set from outside is a change to the document as much as typing
|
|
374
|
+
is — a Text view seeded after mount, a Preview following the author. The
|
|
375
|
+
server colours and diagnoses its own copy of the text, so it must hold
|
|
376
|
+
this one; typing already sent its text, and is not resent. */
|
|
377
|
+
const { didChange: syncDocument } = session;
|
|
378
|
+
useEffect(() => {
|
|
379
|
+
syncDocument(value);
|
|
380
|
+
}, [syncDocument, value]);
|
|
354
381
|
|
|
355
382
|
const diagnosticDecorate = useLspDiagnosticDecorations(
|
|
356
383
|
session.diagnostics,
|
|
@@ -450,15 +477,20 @@ export const SmartEditor = forwardRef<TReqoreRichTextEditorRef, ISmartEditorProp
|
|
|
450
477
|
(newNodes: ISlateElement[]) => {
|
|
451
478
|
const plainText = converter.fromSlateNodes(newNodes);
|
|
452
479
|
|
|
480
|
+
// A selection change reports the same tree; anything else is the tree
|
|
481
|
+
// the editor now holds, and the one every offset is computed against.
|
|
482
|
+
if (newNodes !== slateValueRef.current) {
|
|
483
|
+
slateValueRef.current = newNodes;
|
|
484
|
+
noteEditorTree();
|
|
485
|
+
}
|
|
486
|
+
|
|
453
487
|
if (plainText !== lastPlainTextRef.current) {
|
|
454
488
|
lastPlainTextRef.current = plainText;
|
|
455
|
-
// CRITICAL:
|
|
456
|
-
//
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
// never
|
|
460
|
-
// never advances.
|
|
461
|
-
slateValueRef.current = newNodes;
|
|
489
|
+
// CRITICAL: the next render's `value` equals `lastPlainTextRef` (the
|
|
490
|
+
// typing echo), so `slateValue`'s cache returns the live nodes set
|
|
491
|
+
// above — leave them stale and downstream hooks (signature help,
|
|
492
|
+
// semantic tokens) never recompute, so the signature pill's active
|
|
493
|
+
// parameter never advances.
|
|
462
494
|
onChange(plainText);
|
|
463
495
|
session.didChange(plainText);
|
|
464
496
|
}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// (heartbeat, initialize, document tracking, request dispatch); story files
|
|
3
3
|
// register only their language-specific handlers.
|
|
4
4
|
import { Client, Server } from 'mock-socket';
|
|
5
|
+
import { storySocketUrl } from '../../../stories/storyNetwork';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
/** Where `ReqraftLspClient` dials by default, on the instance stories use. */
|
|
8
|
+
export const MOCK_LSP_URL = storySocketUrl('lsp');
|
|
7
9
|
|
|
8
10
|
// LSP-standard 16-type / 6-modifier legend, as the real Qorus server
|
|
9
11
|
// advertises in `initialize` (qorus/Classes/QorusLspWebSocketHandler.qc:847).
|
|
@@ -76,31 +78,69 @@ export interface IMockLspServer {
|
|
|
76
78
|
received: IMockLspMessage[];
|
|
77
79
|
connectionCount: number;
|
|
78
80
|
getOpenConnectionCount: () => number;
|
|
79
|
-
// Most-recent document text from didOpen / didChange.
|
|
81
|
+
// Most-recent document text from didOpen / didChange, whichever document.
|
|
80
82
|
documentText: string;
|
|
83
|
+
/**
|
|
84
|
+
* The text of every open document, by URI. Clients share ONE socket per
|
|
85
|
+
* endpoint and open a document each — an editor and several read-only
|
|
86
|
+
* renderings at once — so a per-document answer (semantic tokens) must read
|
|
87
|
+
* its own document, not whichever one changed last.
|
|
88
|
+
*/
|
|
89
|
+
textOf: (uri?: string) => string;
|
|
81
90
|
delays: Record<string, number>;
|
|
82
91
|
notify: (method: string, params: unknown) => void;
|
|
83
92
|
close: () => void;
|
|
84
93
|
}
|
|
85
94
|
|
|
95
|
+
/* One mock per URL at a time. `.storybook/preview.tsx` starts a default DPQL
|
|
96
|
+
language server for every story, and a story that needs its own handlers
|
|
97
|
+
creates a server on the same URL; mock-socket refuses a second server there,
|
|
98
|
+
so creating one replaces whichever is listening. */
|
|
99
|
+
const activeServers = new Map<string, IMockLspServer>();
|
|
100
|
+
|
|
86
101
|
export const createMockLspServer = (
|
|
87
102
|
url: string,
|
|
88
103
|
options: IMockLspServerOptions = {}
|
|
89
104
|
): IMockLspServer => {
|
|
105
|
+
// No connection reset here: `.storybook/preview.tsx` resets the shared LSP
|
|
106
|
+
// connections (and the expression render client) before every story, which
|
|
107
|
+
// covers stories that never create a mock server as well as those that do.
|
|
108
|
+
activeServers.get(url)?.close();
|
|
90
109
|
const server = new Server(url);
|
|
91
110
|
const sockets: Client[] = [];
|
|
111
|
+
const documents = new Map<string, string>();
|
|
112
|
+
let closed = false;
|
|
92
113
|
const lsp: IMockLspServer = {
|
|
93
114
|
received: [],
|
|
94
115
|
connectionCount: 0,
|
|
95
116
|
getOpenConnectionCount: () => sockets.filter((socket) => socket.readyState === 1).length,
|
|
96
117
|
documentText: '',
|
|
118
|
+
textOf: (uri) => (uri !== undefined && documents.has(uri) ? documents.get(uri)! : lsp.documentText),
|
|
97
119
|
delays: { ...options.delays },
|
|
98
120
|
notify: (method, params) =>
|
|
99
121
|
sockets.forEach((socket) =>
|
|
100
122
|
socket.send(JSON.stringify({ jsonrpc: '2.0', method, params }))
|
|
101
123
|
),
|
|
102
|
-
close: () =>
|
|
124
|
+
close: () => {
|
|
125
|
+
/* Once only. mock-socket finds clients by URL, not by server, so closing a
|
|
126
|
+
server that was already replaced would close its REPLACEMENT's clients
|
|
127
|
+
— and the default server is closed twice when a story replaces it: once
|
|
128
|
+
on replacement, once in the preview's teardown. */
|
|
129
|
+
if (closed) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
closed = true;
|
|
133
|
+
if (activeServers.get(url) === lsp) {
|
|
134
|
+
activeServers.delete(url);
|
|
135
|
+
}
|
|
136
|
+
server.close();
|
|
137
|
+
// `close()` drops the clients but leaves mock-socket's WebSocket installed
|
|
138
|
+
// on the page; only `stop()` puts the real one back. Without it a story
|
|
139
|
+
// that reaches a real server (`parameters.live`) dials the mock instead.
|
|
140
|
+
server.stop();
|
|
141
|
+
},
|
|
103
142
|
};
|
|
143
|
+
activeServers.set(url, lsp);
|
|
104
144
|
|
|
105
145
|
server.on('connection', (socket) => {
|
|
106
146
|
lsp.connectionCount++;
|
|
@@ -119,14 +159,23 @@ export const createMockLspServer = (
|
|
|
119
159
|
}
|
|
120
160
|
lsp.received.push(msg);
|
|
121
161
|
|
|
162
|
+
const uri = msg.params?.textDocument?.uri;
|
|
122
163
|
if (msg.method === 'textDocument/didOpen') {
|
|
123
164
|
lsp.documentText = msg.params?.textDocument?.text ?? '';
|
|
165
|
+
if (uri !== undefined) {
|
|
166
|
+
documents.set(uri, lsp.documentText);
|
|
167
|
+
}
|
|
124
168
|
} else if (msg.method === 'textDocument/didChange') {
|
|
125
169
|
// The client sends a single full-document content change.
|
|
126
170
|
const change = msg.params?.contentChanges?.[0];
|
|
127
171
|
if (change && typeof change.text === 'string') {
|
|
128
172
|
lsp.documentText = change.text;
|
|
173
|
+
if (uri !== undefined) {
|
|
174
|
+
documents.set(uri, change.text);
|
|
175
|
+
}
|
|
129
176
|
}
|
|
177
|
+
} else if (msg.method === 'textDocument/didClose' && uri !== undefined) {
|
|
178
|
+
documents.delete(uri);
|
|
130
179
|
}
|
|
131
180
|
|
|
132
181
|
const handler = options.handlers?.[msg.method];
|
|
@@ -77,7 +77,11 @@ export interface IUseLspSessionResult {
|
|
|
77
77
|
* only fires when `capabilities?.signatureHelpProvider` exists).
|
|
78
78
|
*/
|
|
79
79
|
capabilities: ILspServerCapabilities | null;
|
|
80
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* The editor now holds `text`: the server is sent it via `didChange`, or via
|
|
82
|
+
* `didOpen` if the document is not open yet. Text the server already holds
|
|
83
|
+
* is not resent, so it is safe to call for every value the editor shows.
|
|
84
|
+
*/
|
|
81
85
|
didChange: (text: string) => void;
|
|
82
86
|
/**
|
|
83
87
|
* Request `textDocument/completion` at a plain-text offset. Converts
|
|
@@ -96,6 +100,13 @@ export function useLspSession(
|
|
|
96
100
|
|
|
97
101
|
const clientRef = useRef<ReqraftLspClient | null>(null);
|
|
98
102
|
const versionRef = useRef(1);
|
|
103
|
+
/* The text the editor holds, and the text the server holds for this
|
|
104
|
+
document — `null` until the document is open. The server tokenises and
|
|
105
|
+
diagnoses ITS copy, so every text the editor shows has to reach it, however
|
|
106
|
+
it got there: typed, or set from outside after mount (a Text view seeded
|
|
107
|
+
once `dpql/serialize` answers, a Preview following the typing). */
|
|
108
|
+
const editorTextRef = useRef(initialText);
|
|
109
|
+
const serverTextRef = useRef<string | null>(null);
|
|
99
110
|
const uriRef = useRef(uriProp ?? `${languageId}://session/${++lspUriCounter}`);
|
|
100
111
|
|
|
101
112
|
const [client, setClient] = useState<ReqraftLspClient | null>(null);
|
|
@@ -129,8 +140,11 @@ export function useLspSession(
|
|
|
129
140
|
// result.
|
|
130
141
|
setSemanticTokensLegend(c.semanticTokensLegend);
|
|
131
142
|
setCapabilities(c.capabilities);
|
|
143
|
+
// Opened with the text the editor holds NOW, which may have been set
|
|
144
|
+
// while the connection was opening.
|
|
145
|
+
serverTextRef.current = editorTextRef.current;
|
|
132
146
|
c.didOpen(
|
|
133
|
-
|
|
147
|
+
editorTextRef.current,
|
|
134
148
|
initialMetadata && Object.keys(initialMetadata).length > 0
|
|
135
149
|
? initialMetadata
|
|
136
150
|
: undefined
|
|
@@ -146,6 +160,7 @@ export function useLspSession(
|
|
|
146
160
|
return () => {
|
|
147
161
|
c.disconnect();
|
|
148
162
|
clientRef.current = null;
|
|
163
|
+
serverTextRef.current = null;
|
|
149
164
|
setClient(null);
|
|
150
165
|
setIsReady(false);
|
|
151
166
|
setSemanticTokensLegend(null);
|
|
@@ -158,8 +173,11 @@ export function useLspSession(
|
|
|
158
173
|
}, []);
|
|
159
174
|
|
|
160
175
|
const didChange = useCallback((text: string) => {
|
|
176
|
+
editorTextRef.current = text;
|
|
161
177
|
const c = clientRef.current;
|
|
162
|
-
|
|
178
|
+
// Not open yet: `didOpen` will carry it. Already held: nothing to send.
|
|
179
|
+
if (!c || serverTextRef.current === null || serverTextRef.current === text) return;
|
|
180
|
+
serverTextRef.current = text;
|
|
163
181
|
c.didChange(text, ++versionRef.current);
|
|
164
182
|
}, []);
|
|
165
183
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { TReqoreDropdownItems } from '@qoretechnologies/reqore/dist/components/Dropdown/list';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
3
|
import { Composer, IComposerSendAction, TComposerEditor } from '../composer/Composer';
|
|
4
4
|
import { fileToAttachment as defaultFileToAttachment } from './fileToAttachment';
|
|
5
5
|
import { IAttachmentUpload } from './meta';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* The `items` shape ReqoreDropdown accepts. Named, not derived: `ReqoreDropdown`
|
|
9
|
+
* is a generic component, and `ComponentProps` of a generic resolves to `{}`
|
|
10
|
+
* under strict checking — so the derived type had no `items` at all.
|
|
11
|
+
*/
|
|
12
|
+
type TAttachMenuItems = TReqoreDropdownItems;
|
|
9
13
|
|
|
10
14
|
export interface ITicketReplyBoxProps {
|
|
11
15
|
/** Send the composed message. `internal` is only ever true when
|
|
@@ -4,6 +4,7 @@ import styled from 'styled-components';
|
|
|
4
4
|
|
|
5
5
|
import { thinScrollbar } from '../../helpers/scrollbar';
|
|
6
6
|
import { StoryMeta } from '../../types';
|
|
7
|
+
import type { IInterfaceReference, TTicketMessageAuthor } from '../supportTicket/meta';
|
|
7
8
|
import { ITicketThreadMessage, TicketThread } from './TicketThread';
|
|
8
9
|
|
|
9
10
|
/* The drawer body's scroll area — the thin Qorus scrollbar rather than the platform
|
package/src/helpers/common.ts
CHANGED
|
@@ -79,11 +79,15 @@ export const getDefaultValue = (schema?: TQorusFormFieldSchema): unknown | undef
|
|
|
79
79
|
return undefined;
|
|
80
80
|
}
|
|
81
81
|
if ('default_value' in schema) {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
const value: unknown = schema.default_value;
|
|
83
|
+
// The server sends a default either bare, of any type — a list or a hash
|
|
84
|
+
// included — or in a `{ type, value }` envelope; only the envelope is
|
|
85
|
+
// unwrapped.
|
|
86
|
+
if (value != null && typeof value === 'object' && !Array.isArray(value) && 'type' in value) {
|
|
87
|
+
return (value as { value?: unknown }).value;
|
|
84
88
|
}
|
|
85
89
|
|
|
86
|
-
return
|
|
90
|
+
return value;
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
return undefined;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// Copyright 2026 Qore Technologies, s.r.o.
|
|
2
|
+
/**
|
|
3
|
+
* Noticing that text typed into a PLAIN (non-expression) field is really a
|
|
4
|
+
* DPQL expression, and deciding what to do about it.
|
|
5
|
+
*
|
|
6
|
+
* The rule this implements, in one sentence: **switch the field into
|
|
7
|
+
* expression mode only when the text cannot be a valid literal for the
|
|
8
|
+
* field's type but IS a real expression; otherwise offer, never silently
|
|
9
|
+
* reinterpret.** Quietly turning the string `a + b` into a concatenation on a
|
|
10
|
+
* `string` field would destroy a legitimate literal, so a type that accepts
|
|
11
|
+
* any text never auto-switches — it only ever offers.
|
|
12
|
+
*
|
|
13
|
+
* ## Why the server has the last word
|
|
14
|
+
*
|
|
15
|
+
* "Does this parse as DPQL?" is NOT a usable test, which is the trap here.
|
|
16
|
+
* Verified against the live LSP: `dpql/parse` reports `success: true` for
|
|
17
|
+
* `hello`, for `42`, for `2026-09-06` and for `@a` — it wraps each of them in
|
|
18
|
+
* a trivial `{exp: 'value', args: [...]}` node. Everything is a valid DPQL
|
|
19
|
+
* program, because a bare literal is one. So a text that parses tells us
|
|
20
|
+
* nothing; what tells us something is the SHAPE of the AST that comes back:
|
|
21
|
+
*
|
|
22
|
+
* | typed text | `exp` | verdict |
|
|
23
|
+
* |-------------------|------------|------------|
|
|
24
|
+
* | `hello`, `42` | `value` | a literal |
|
|
25
|
+
* | `$local:count` | `template` | a template |
|
|
26
|
+
* | `@a > 5` | `>` | expression |
|
|
27
|
+
* | `"a" + "b"` | `+` | expression |
|
|
28
|
+
* | `toInt("5")` | `toInt` | expression |
|
|
29
|
+
*
|
|
30
|
+
* `isRealExpressionAst` is that test, and it is the only authority. The
|
|
31
|
+
* lexical pass below decides nothing — its sole job is to keep a server
|
|
32
|
+
* round-trip off every keystroke of every field.
|
|
33
|
+
*/
|
|
34
|
+
import { validateField } from './validations';
|
|
35
|
+
|
|
36
|
+
/** What to do about text that turned out to be an expression. */
|
|
37
|
+
export type TDpqlDetectionOutcome =
|
|
38
|
+
/** Not an expression, or not one we may act on — leave the field alone. */
|
|
39
|
+
| 'none'
|
|
40
|
+
/** A real expression, but the text is also a valid literal here: ask. */
|
|
41
|
+
| 'offer'
|
|
42
|
+
/** A real expression, and the text cannot be a literal here: switch. */
|
|
43
|
+
| 'switch';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The `exp` values that mean "this is not an expression": a bare literal and
|
|
47
|
+
* a bare template reference. A template is already the template field's job
|
|
48
|
+
* (`isValueTemplate`), and a literal is what the plain field is for.
|
|
49
|
+
*/
|
|
50
|
+
const NON_EXPRESSION_OPS = new Set(['value', 'template']);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Operators and calls whose presence makes a server probe worth the trip.
|
|
54
|
+
* Deliberately generous — a false positive costs one debounced request that
|
|
55
|
+
* comes back `exp: 'value'`, while a false negative silently loses the whole
|
|
56
|
+
* feature.
|
|
57
|
+
*
|
|
58
|
+
* ## Why the spacing rules differ per operator
|
|
59
|
+
*
|
|
60
|
+
* `-` and `/` are ambiguous: they appear inside values nobody means as
|
|
61
|
+
* arithmetic — dates (`2026-09-06`), negative numbers (`-5`), paths (`a/b`),
|
|
62
|
+
* hyphenated names (`well-known-name`). A space next to one is what separates
|
|
63
|
+
* `@a - 1` from those, so they keep a spacing requirement.
|
|
64
|
+
*
|
|
65
|
+
* `+` is NOT ambiguous in that way, and requiring spaces around it lost the
|
|
66
|
+
* feature for the most natural way to type arithmetic: `2 + 1` was offered as
|
|
67
|
+
* an expression while `2+1` and `2+ 1` were not, which reads as the detection
|
|
68
|
+
* working only sometimes. Reported from the live IDE. `+` now counts wherever
|
|
69
|
+
* it appears; if the text turns out to be an ordinary literal the server says
|
|
70
|
+
* so and the offer never appears.
|
|
71
|
+
*
|
|
72
|
+
* The `-`/`/` rule is also relaxed from "spaces on BOTH sides" to "a space on
|
|
73
|
+
* either side", so `@a -1` and `@a- 1` are probed too while every ambiguous
|
|
74
|
+
* value above still is not.
|
|
75
|
+
*/
|
|
76
|
+
const DPQL_OPERATOR_PATTERN = new RegExp(
|
|
77
|
+
[
|
|
78
|
+
'(==|!=|<>|>=|<=)', // two-character comparisons
|
|
79
|
+
'[<>*%]', // unambiguous single-character operators
|
|
80
|
+
'(?<!=)=(?!=)', // a lone `=`, not part of `==`
|
|
81
|
+
'\\+', // `+` — unambiguous, so spacing is irrelevant
|
|
82
|
+
'(\\s[-/]|[-/]\\s)', // `-` and `/` need a space on at least one side
|
|
83
|
+
'\\b(and|or|not|like|in|contains)\\b', // word operators
|
|
84
|
+
].join('|'),
|
|
85
|
+
'i'
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
/** A function call — `toInt("5")`, `upper(@name)`. */
|
|
89
|
+
const DPQL_CALL_PATTERN = /\b[a-z_]\w*\s*\(/i;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The longest text worth probing. An expression a person types into a form
|
|
93
|
+
* field is short; anything longer is a document, and probing it on every
|
|
94
|
+
* keystroke would be a waste of a round-trip.
|
|
95
|
+
*/
|
|
96
|
+
const MAX_PROBE_LENGTH = 512;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Cheap, purely lexical "might this be worth asking the server about?".
|
|
100
|
+
* Never decides anything on its own — see the module comment.
|
|
101
|
+
*/
|
|
102
|
+
export const mightBeDpqlExpression = (text: unknown): boolean => {
|
|
103
|
+
if (typeof text !== 'string') {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const trimmed = text.trim();
|
|
108
|
+
|
|
109
|
+
if (!trimmed || trimmed.length > MAX_PROBE_LENGTH) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return DPQL_OPERATOR_PATTERN.test(trimmed) || DPQL_CALL_PATTERN.test(trimmed);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The authority: does this `dpql/parse` result describe a REAL expression
|
|
118
|
+
* rather than a literal or a bare template reference? Accepts either the
|
|
119
|
+
* field-ready envelope (`{is_expression, value: {exp, args}}`) the server
|
|
120
|
+
* returns or a bare `{exp, args}` node.
|
|
121
|
+
*/
|
|
122
|
+
export const isRealExpressionAst = (expression: unknown): boolean => {
|
|
123
|
+
if (!expression || typeof expression !== 'object') {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const envelope = expression as { value?: unknown; exp?: unknown };
|
|
128
|
+
const node = (
|
|
129
|
+
envelope.exp === undefined && envelope.value && typeof envelope.value === 'object'
|
|
130
|
+
? envelope.value
|
|
131
|
+
: envelope
|
|
132
|
+
) as { exp?: unknown };
|
|
133
|
+
|
|
134
|
+
return typeof node.exp === 'string' && !!node.exp && !NON_EXPRESSION_OPS.has(node.exp);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* `true` when any diagnostic is an outright error. The server reports
|
|
139
|
+
* severity as the string `'error'` on this route and LSP proper uses `1`;
|
|
140
|
+
* both are accepted rather than assuming one.
|
|
141
|
+
*/
|
|
142
|
+
export const hasErrorDiagnostic = (
|
|
143
|
+
diagnostics?: Array<{ severity?: number | string } | undefined | null>
|
|
144
|
+
): boolean =>
|
|
145
|
+
!!diagnostics?.some((diagnostic) => {
|
|
146
|
+
const severity = diagnostic?.severity;
|
|
147
|
+
return severity === 1 || severity === 'error';
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Could this text stand as a literal value of the field's own type? A type
|
|
152
|
+
* that accepts any text (`string`, and every type the form validator passes
|
|
153
|
+
* by default) always can, which is exactly why such a field never
|
|
154
|
+
* auto-switches. Delegates to the form's own validator so the answer is the
|
|
155
|
+
* same one the field itself would give.
|
|
156
|
+
*/
|
|
157
|
+
export const canBeLiteralOfType = (
|
|
158
|
+
text: string,
|
|
159
|
+
type?: string,
|
|
160
|
+
field?: Record<string, any>
|
|
161
|
+
): boolean => {
|
|
162
|
+
if (!type) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
return validateField(type, text, field as any);
|
|
168
|
+
} catch {
|
|
169
|
+
// A validator that throws must not be read as "this cannot be a
|
|
170
|
+
// literal" — that would auto-switch the field on the strength of a bug.
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export interface IClassifyTypedTextArgs {
|
|
176
|
+
/** The text the author typed into the plain field. */
|
|
177
|
+
text: string;
|
|
178
|
+
/** The field's declared type (`ui_type ?? type`). */
|
|
179
|
+
type?: string;
|
|
180
|
+
/** The field schema, for validators that consult `validation_regex` etc. */
|
|
181
|
+
field?: Record<string, any>;
|
|
182
|
+
/** The `dpql/parse` result for `text`, or `undefined` if it has not run. */
|
|
183
|
+
parsed?: {
|
|
184
|
+
success?: boolean;
|
|
185
|
+
expression?: unknown;
|
|
186
|
+
diagnostics?: Array<{ severity?: number | string }>;
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The whole decision, in one pure function so it can be tested without a
|
|
192
|
+
* server, a socket or a rendered field.
|
|
193
|
+
*/
|
|
194
|
+
export const classifyTypedText = ({
|
|
195
|
+
text,
|
|
196
|
+
type,
|
|
197
|
+
field,
|
|
198
|
+
parsed,
|
|
199
|
+
}: IClassifyTypedTextArgs): TDpqlDetectionOutcome => {
|
|
200
|
+
if (!parsed?.success || !parsed.expression) {
|
|
201
|
+
return 'none';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!isRealExpressionAst(parsed.expression) || hasErrorDiagnostic(parsed.diagnostics)) {
|
|
205
|
+
return 'none';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return canBeLiteralOfType(text, type, field) ? 'offer' : 'switch';
|
|
209
|
+
};
|