slackblocks 3.1.0 → 3.2.0
raw patch · 26 files changed
+2002/−46 lines, 26 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Slackblocks.Models: LayoutAlignmentCenter :: LayoutAlignment
+ Slackblocks.Models: LayoutAlignmentEnd :: LayoutAlignment
+ Slackblocks.Models: LayoutAlignmentStart :: LayoutAlignment
+ Slackblocks.Models: LayoutBlockFields :: Text -> [LayoutRow] -> Maybe Bool -> Maybe LayoutGridLines -> Maybe [LayoutFallback] -> Maybe Text -> Map Text Value -> LayoutBlockFields
+ Slackblocks.Models: LayoutGridLinesNone :: LayoutGridLines
+ Slackblocks.Models: LayoutGridLinesOuter :: LayoutGridLines
+ Slackblocks.Models: LayoutGridLinesRows :: LayoutGridLines
+ Slackblocks.Models: LayoutItemFields :: LayoutChild -> Maybe LayoutWidth -> Maybe LayoutAlignment -> Map Text Value -> LayoutItemFields
+ Slackblocks.Models: LayoutRowFields :: [LayoutItem] -> Map Text Value -> LayoutRowFields
+ Slackblocks.Models: LayoutWidthNarrow :: LayoutWidth
+ Slackblocks.Models: LayoutWidthStandard :: LayoutWidth
+ Slackblocks.Models: LayoutWidthWide :: LayoutWidth
+ Slackblocks.Models: [layoutBlockBlockId] :: LayoutBlockFields -> Maybe Text
+ Slackblocks.Models: [layoutBlockExtensions] :: LayoutBlockFields -> Map Text Value
+ Slackblocks.Models: [layoutBlockFallback] :: LayoutBlockFields -> Maybe [LayoutFallback]
+ Slackblocks.Models: [layoutBlockGridLines] :: LayoutBlockFields -> Maybe LayoutGridLines
+ Slackblocks.Models: [layoutBlockIsLabelVisible] :: LayoutBlockFields -> Maybe Bool
+ Slackblocks.Models: [layoutBlockLabel] :: LayoutBlockFields -> Text
+ Slackblocks.Models: [layoutBlockRows] :: LayoutBlockFields -> [LayoutRow]
+ Slackblocks.Models: [layoutItemBlock] :: LayoutItemFields -> LayoutChild
+ Slackblocks.Models: [layoutItemExtensions] :: LayoutItemFields -> Map Text Value
+ Slackblocks.Models: [layoutItemHorizontalAlignment] :: LayoutItemFields -> Maybe LayoutAlignment
+ Slackblocks.Models: [layoutItemWidth] :: LayoutItemFields -> Maybe LayoutWidth
+ Slackblocks.Models: [layoutRowExtensions] :: LayoutRowFields -> Map Text Value
+ Slackblocks.Models: [layoutRowItems] :: LayoutRowFields -> [LayoutItem]
+ Slackblocks.Models: actionsBlockAsLayoutChild :: ActionsBlock -> LayoutChild
+ Slackblocks.Models: alertBlockAsLayoutChild :: AlertBlock -> LayoutChild
+ Slackblocks.Models: buildLayoutBlock :: LayoutBlockFields -> Result LayoutBlock
+ Slackblocks.Models: buildLayoutItem :: LayoutItemFields -> Result LayoutItem
+ Slackblocks.Models: buildLayoutRow :: LayoutRowFields -> Result LayoutRow
+ Slackblocks.Models: cardBlockAsLayoutChild :: CardBlock -> LayoutChild
+ Slackblocks.Models: carouselBlockAsLayoutChild :: CarouselBlock -> LayoutChild
+ Slackblocks.Models: containerBlockAsLayoutChild :: ContainerBlock -> LayoutChild
+ Slackblocks.Models: contextBlockAsLayoutChild :: ContextBlock -> LayoutChild
+ Slackblocks.Models: contextBlockAsLayoutFallback :: ContextBlock -> LayoutFallback
+ Slackblocks.Models: data LayoutAlignment
+ Slackblocks.Models: data LayoutBlock
+ Slackblocks.Models: data LayoutBlockFields
+ Slackblocks.Models: data LayoutChild
+ Slackblocks.Models: data LayoutFallback
+ Slackblocks.Models: data LayoutGridLines
+ Slackblocks.Models: data LayoutItem
+ Slackblocks.Models: data LayoutItemFields
+ Slackblocks.Models: data LayoutRow
+ Slackblocks.Models: data LayoutRowFields
+ Slackblocks.Models: data LayoutWidth
+ Slackblocks.Models: dataTableBlockAsLayoutChild :: DataTableBlock -> LayoutChild
+ Slackblocks.Models: dataVisualizationBlockAsLayoutChild :: DataVisualizationBlock -> LayoutChild
+ Slackblocks.Models: dividerBlockAsLayoutChild :: DividerBlock -> LayoutChild
+ Slackblocks.Models: dividerBlockAsLayoutFallback :: DividerBlock -> LayoutFallback
+ Slackblocks.Models: fileBlockAsLayoutChild :: FileBlock -> LayoutChild
+ Slackblocks.Models: headerBlockAsLayoutChild :: HeaderBlock -> LayoutChild
+ Slackblocks.Models: headerBlockAsLayoutFallback :: HeaderBlock -> LayoutFallback
+ Slackblocks.Models: imageBlockAsLayoutChild :: ImageBlock -> LayoutChild
+ Slackblocks.Models: imageBlockAsLayoutFallback :: ImageBlock -> LayoutFallback
+ Slackblocks.Models: inputBlockAsLayoutChild :: InputBlock -> LayoutChild
+ Slackblocks.Models: inspectLayoutBlock :: LayoutBlock -> LayoutBlockFields
+ Slackblocks.Models: inspectLayoutItem :: LayoutItem -> LayoutItemFields
+ Slackblocks.Models: inspectLayoutRow :: LayoutRow -> LayoutRowFields
+ Slackblocks.Models: instance Data.Aeson.Types.FromJSON.FromJSON Slackblocks.Models.LayoutAlignment
+ Slackblocks.Models: instance Data.Aeson.Types.FromJSON.FromJSON Slackblocks.Models.LayoutBlock
+ Slackblocks.Models: instance Data.Aeson.Types.FromJSON.FromJSON Slackblocks.Models.LayoutChild
+ Slackblocks.Models: instance Data.Aeson.Types.FromJSON.FromJSON Slackblocks.Models.LayoutFallback
+ Slackblocks.Models: instance Data.Aeson.Types.FromJSON.FromJSON Slackblocks.Models.LayoutGridLines
+ Slackblocks.Models: instance Data.Aeson.Types.FromJSON.FromJSON Slackblocks.Models.LayoutItem
+ Slackblocks.Models: instance Data.Aeson.Types.FromJSON.FromJSON Slackblocks.Models.LayoutRow
+ Slackblocks.Models: instance Data.Aeson.Types.FromJSON.FromJSON Slackblocks.Models.LayoutWidth
+ Slackblocks.Models: instance Data.Aeson.Types.ToJSON.ToJSON Slackblocks.Models.LayoutAlignment
+ Slackblocks.Models: instance Data.Aeson.Types.ToJSON.ToJSON Slackblocks.Models.LayoutBlock
+ Slackblocks.Models: instance Data.Aeson.Types.ToJSON.ToJSON Slackblocks.Models.LayoutChild
+ Slackblocks.Models: instance Data.Aeson.Types.ToJSON.ToJSON Slackblocks.Models.LayoutFallback
+ Slackblocks.Models: instance Data.Aeson.Types.ToJSON.ToJSON Slackblocks.Models.LayoutGridLines
+ Slackblocks.Models: instance Data.Aeson.Types.ToJSON.ToJSON Slackblocks.Models.LayoutItem
+ Slackblocks.Models: instance Data.Aeson.Types.ToJSON.ToJSON Slackblocks.Models.LayoutRow
+ Slackblocks.Models: instance Data.Aeson.Types.ToJSON.ToJSON Slackblocks.Models.LayoutWidth
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutAlignment
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutBlock
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutBlockFields
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutChild
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutFallback
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutGridLines
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutItem
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutItemFields
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutRow
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutRowFields
+ Slackblocks.Models: instance GHC.Classes.Eq Slackblocks.Models.LayoutWidth
+ Slackblocks.Models: instance GHC.Classes.Ord Slackblocks.Models.LayoutAlignment
+ Slackblocks.Models: instance GHC.Classes.Ord Slackblocks.Models.LayoutGridLines
+ Slackblocks.Models: instance GHC.Classes.Ord Slackblocks.Models.LayoutWidth
+ Slackblocks.Models: instance GHC.Enum.Bounded Slackblocks.Models.LayoutAlignment
+ Slackblocks.Models: instance GHC.Enum.Bounded Slackblocks.Models.LayoutGridLines
+ Slackblocks.Models: instance GHC.Enum.Bounded Slackblocks.Models.LayoutWidth
+ Slackblocks.Models: instance GHC.Enum.Enum Slackblocks.Models.LayoutAlignment
+ Slackblocks.Models: instance GHC.Enum.Enum Slackblocks.Models.LayoutGridLines
+ Slackblocks.Models: instance GHC.Enum.Enum Slackblocks.Models.LayoutWidth
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutAlignment
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutBlock
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutBlockFields
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutChild
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutFallback
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutGridLines
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutItem
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutItemFields
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutRow
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutRowFields
+ Slackblocks.Models: instance GHC.Show.Show Slackblocks.Models.LayoutWidth
+ Slackblocks.Models: instance Slackblocks.Json.FromSlackJSON Slackblocks.Models.LayoutAlignment
+ Slackblocks.Models: instance Slackblocks.Json.FromSlackJSON Slackblocks.Models.LayoutBlock
+ Slackblocks.Models: instance Slackblocks.Json.FromSlackJSON Slackblocks.Models.LayoutChild
+ Slackblocks.Models: instance Slackblocks.Json.FromSlackJSON Slackblocks.Models.LayoutFallback
+ Slackblocks.Models: instance Slackblocks.Json.FromSlackJSON Slackblocks.Models.LayoutGridLines
+ Slackblocks.Models: instance Slackblocks.Json.FromSlackJSON Slackblocks.Models.LayoutItem
+ Slackblocks.Models: instance Slackblocks.Json.FromSlackJSON Slackblocks.Models.LayoutRow
+ Slackblocks.Models: instance Slackblocks.Json.FromSlackJSON Slackblocks.Models.LayoutWidth
+ Slackblocks.Models: layoutBlock :: Text -> [LayoutRow] -> LayoutBlockFields
+ Slackblocks.Models: layoutBlockAsBlock :: LayoutBlock -> Block
+ Slackblocks.Models: layoutItem :: LayoutChild -> LayoutItemFields
+ Slackblocks.Models: layoutRow :: [LayoutItem] -> LayoutRowFields
+ Slackblocks.Models: matchBlockLayoutBlock :: Block -> Maybe LayoutBlock
+ Slackblocks.Models: matchLayoutChildActionsBlock :: LayoutChild -> Maybe ActionsBlock
+ Slackblocks.Models: matchLayoutChildAlertBlock :: LayoutChild -> Maybe AlertBlock
+ Slackblocks.Models: matchLayoutChildCardBlock :: LayoutChild -> Maybe CardBlock
+ Slackblocks.Models: matchLayoutChildCarouselBlock :: LayoutChild -> Maybe CarouselBlock
+ Slackblocks.Models: matchLayoutChildContainerBlock :: LayoutChild -> Maybe ContainerBlock
+ Slackblocks.Models: matchLayoutChildContextBlock :: LayoutChild -> Maybe ContextBlock
+ Slackblocks.Models: matchLayoutChildDataTableBlock :: LayoutChild -> Maybe DataTableBlock
+ Slackblocks.Models: matchLayoutChildDataVisualizationBlock :: LayoutChild -> Maybe DataVisualizationBlock
+ Slackblocks.Models: matchLayoutChildDividerBlock :: LayoutChild -> Maybe DividerBlock
+ Slackblocks.Models: matchLayoutChildFileBlock :: LayoutChild -> Maybe FileBlock
+ Slackblocks.Models: matchLayoutChildHeaderBlock :: LayoutChild -> Maybe HeaderBlock
+ Slackblocks.Models: matchLayoutChildImageBlock :: LayoutChild -> Maybe ImageBlock
+ Slackblocks.Models: matchLayoutChildInputBlock :: LayoutChild -> Maybe InputBlock
+ Slackblocks.Models: matchLayoutChildRichTextBlock :: LayoutChild -> Maybe RichTextBlock
+ Slackblocks.Models: matchLayoutChildSectionBlock :: LayoutChild -> Maybe SectionBlock
+ Slackblocks.Models: matchLayoutChildTableBlock :: LayoutChild -> Maybe TableBlock
+ Slackblocks.Models: matchLayoutChildVideoBlock :: LayoutChild -> Maybe VideoBlock
+ Slackblocks.Models: matchLayoutFallbackContextBlock :: LayoutFallback -> Maybe ContextBlock
+ Slackblocks.Models: matchLayoutFallbackDividerBlock :: LayoutFallback -> Maybe DividerBlock
+ Slackblocks.Models: matchLayoutFallbackHeaderBlock :: LayoutFallback -> Maybe HeaderBlock
+ Slackblocks.Models: matchLayoutFallbackImageBlock :: LayoutFallback -> Maybe ImageBlock
+ Slackblocks.Models: matchLayoutFallbackRichTextBlock :: LayoutFallback -> Maybe RichTextBlock
+ Slackblocks.Models: matchLayoutFallbackSectionBlock :: LayoutFallback -> Maybe SectionBlock
+ Slackblocks.Models: matchLayoutFallbackTableBlock :: LayoutFallback -> Maybe TableBlock
+ Slackblocks.Models: matchLayoutFallbackVideoBlock :: LayoutFallback -> Maybe VideoBlock
+ Slackblocks.Models: richTextBlockAsLayoutChild :: RichTextBlock -> LayoutChild
+ Slackblocks.Models: richTextBlockAsLayoutFallback :: RichTextBlock -> LayoutFallback
+ Slackblocks.Models: sectionBlockAsLayoutChild :: SectionBlock -> LayoutChild
+ Slackblocks.Models: sectionBlockAsLayoutFallback :: SectionBlock -> LayoutFallback
+ Slackblocks.Models: tableBlockAsLayoutChild :: TableBlock -> LayoutChild
+ Slackblocks.Models: tableBlockAsLayoutFallback :: TableBlock -> LayoutFallback
+ Slackblocks.Models: videoBlockAsLayoutChild :: VideoBlock -> LayoutChild
+ Slackblocks.Models: videoBlockAsLayoutFallback :: VideoBlock -> LayoutFallback
Files
- CHANGELOG.md +6/−0
- README.md +5/−5
- bin/check +4/−0
- conformance/public-api.json +209/−0
- generator/generate.py +1/−1
- integrations/transport.cabal +1/−1
- slackblocks.cabal +1/−1
- src/Slackblocks/Internal/Rules.hs +36/−4
- src/Slackblocks/Internal/Schema.hs +7/−4
- src/Slackblocks/Models.hs +624/−3
- test/Conformance.hs +1/−1
- test/Constructions.hs +347/−2
- test/Foundation.hs +14/−1
- test/spec/coverage.json +6/−3
- test/spec/fixtures/invalid/manifest.json +127/−1
- test/spec/fixtures/valid/blocks/container_layout.json +31/−0
- test/spec/fixtures/valid/blocks/layout_block.json +17/−0
- test/spec/fixtures/valid/blocks/layout_chart_majority.json +35/−0
- test/spec/fixtures/valid/blocks/layout_container.json +31/−0
- test/spec/fixtures/valid/blocks/layout_empty_fallback.json +20/−0
- test/spec/fixtures/valid/blocks/layout_limits.json +118/−0
- test/spec/fixtures/valid/blocks/layout_styled.json +43/−0
- test/spec/limits.json +5/−0
- test/spec/manifest.json +43/−1
- test/spec/model.json +267/−18
- test/spec/vocabulary.json +3/−0
CHANGELOG.md view
@@ -1,5 +1,11 @@ # Changelog +## [3.2.0] — 2026-10-31++- Add native layout blocks, rows and items with typed widths/alignment, closed child and fallback roles, checked nesting and receiving-surface validation (spec 1.5.0).+- Preserve confirmed top-level message/view block counting, supported runtime minimums, legacy container-only behavior, and existing chart/text limits. No unconfirmed recursive block budget is imposed.+- Add a runnable ten-language layout recipe and rendered preview; preserve outgoing 3.1.0 documentation.+ ## [3.1.0] — 2026-10-28 - Add the complete shared Block Kit contract with checked immutable values, native field records, closed roles and Aeson encoding.
README.md view
@@ -4,9 +4,9 @@ encoding. Requires GHC 9.6.7 or newer within the tested compiler range. The native API covers all 89 shared models, closed roles and enumerations in-contract 1.4.0. Haskell joins the coordinated 3.1.0 release on+contract 1.5.0. Haskell joins the coordinated 3.1.0 release on [Hackage](https://hackage.haskell.org/package/slackblocks). Add-`slackblocks >= 3.1 && < 3.2` to your application's Cabal `build-depends`,+`slackblocks >= 3.2 && < 3.3` to your application's Cabal `build-depends`, run `cabal update`, then `cabal build`. The source archive and native HTTP integration are checked on supported compiler endpoints. @@ -73,10 +73,10 @@ them into potentially enormous integers. Run `cabal test all` from the package directory. Tests use local data and make no-Slack requests. The package version is the coordinated 3.1.0 release;-the independent shared contract version is 1.4.0.+Slack requests. The package version is the coordinated 3.2.0 release;+the independent shared contract version is 1.5.0. -The conformance suite constructs all 113 valid fixtures through native APIs and+The conformance suite constructs all 120 valid fixtures through native APIs and independently checks their wire objects. It also checks every invalid input at JSON ingress, native invalid constructions where representable, and compiler rejections where the type system prevents construction. `python bin/check`
bin/check view
@@ -53,6 +53,10 @@ ("plan-missing-tasks", "import Slackblocks\nf title = buildPlanBlock (planBlock title)\nmain = pure ()\n", "PlanBlockFields"), ("task-card-missing-status", "import Slackblocks\nf identifier title = buildTaskCardBlock (taskCardBlock identifier title)\nmain = pure ()\n", "TaskCardBlockFields"), ("attachment-missing-blocks", "import Slackblocks\nf = buildAttachment attachment\nmain = pure ()\n", "AttachmentFields"),+ ("layout-direct-child", "import Slackblocks\nf :: LayoutBlock -> LayoutItemFields\nf b = layoutItem (layoutBlockAsBlock b)\nmain = pure ()\n", "LayoutChild"),+ ("layout-markdown-child", "import Slackblocks\nf :: MarkdownBlock -> LayoutItemFields\nf b = layoutItem (markdownBlockAsBlock b)\nmain = pure ()\n", "LayoutChild"),+ ("layout-actions-fallback", "import Slackblocks\nf :: ActionsBlock -> LayoutBlockFields -> LayoutBlockFields\nf b f = f {layoutBlockFallback = Just [actionsBlockAsBlock b]}\nmain = pure ()\n", "LayoutFallback"),+ ("layout-full-width", "import Slackblocks\nf x = x {layoutItemWidth = Just ContainerWidthFull}\nmain = pure ()\n", "LayoutWidth"), ("plan-task-as-block", "import Slackblocks\nf :: PlanTask -> Block\nf = taskCardBlockAsBlock\nmain = pure ()\n", "PlanTask"), ]: path = Path(tmp, name + ".hs")
conformance/public-api.json view
@@ -157,6 +157,26 @@ "Slackblocks.Models.InputElement", "Slackblocks.Models.InputParameter", "Slackblocks.Models.InputParameterFields",+ "Slackblocks.Models.LayoutAlignment",+ "Slackblocks.Models.LayoutAlignmentCenter",+ "Slackblocks.Models.LayoutAlignmentEnd",+ "Slackblocks.Models.LayoutAlignmentStart",+ "Slackblocks.Models.LayoutBlock",+ "Slackblocks.Models.LayoutBlockFields",+ "Slackblocks.Models.LayoutChild",+ "Slackblocks.Models.LayoutFallback",+ "Slackblocks.Models.LayoutGridLines",+ "Slackblocks.Models.LayoutGridLinesNone",+ "Slackblocks.Models.LayoutGridLinesOuter",+ "Slackblocks.Models.LayoutGridLinesRows",+ "Slackblocks.Models.LayoutItem",+ "Slackblocks.Models.LayoutItemFields",+ "Slackblocks.Models.LayoutRow",+ "Slackblocks.Models.LayoutRowFields",+ "Slackblocks.Models.LayoutWidth",+ "Slackblocks.Models.LayoutWidthNarrow",+ "Slackblocks.Models.LayoutWidthStandard",+ "Slackblocks.Models.LayoutWidthWide", "Slackblocks.Models.LineChart", "Slackblocks.Models.LineChartFields", "Slackblocks.Models.MarkdownBlock",@@ -267,11 +287,13 @@ "Slackblocks.Models.WorkflowFields", "Slackblocks.Models.actionsBlock", "Slackblocks.Models.actionsBlockAsBlock",+ "Slackblocks.Models.actionsBlockAsLayoutChild", "Slackblocks.Models.actionsBlockBlockId", "Slackblocks.Models.actionsBlockElements", "Slackblocks.Models.actionsBlockExtensions", "Slackblocks.Models.alertBlock", "Slackblocks.Models.alertBlockAsBlock",+ "Slackblocks.Models.alertBlockAsLayoutChild", "Slackblocks.Models.alertBlockBlockId", "Slackblocks.Models.alertBlockExtensions", "Slackblocks.Models.alertBlockLevel",@@ -339,6 +361,9 @@ "Slackblocks.Models.buildImageElement", "Slackblocks.Models.buildInputBlock", "Slackblocks.Models.buildInputParameter",+ "Slackblocks.Models.buildLayoutBlock",+ "Slackblocks.Models.buildLayoutItem",+ "Slackblocks.Models.buildLayoutRow", "Slackblocks.Models.buildLineChart", "Slackblocks.Models.buildMarkdownBlock", "Slackblocks.Models.buildMarkdownText",@@ -401,6 +426,7 @@ "Slackblocks.Models.cardBlock", "Slackblocks.Models.cardBlockActions", "Slackblocks.Models.cardBlockAsBlock",+ "Slackblocks.Models.cardBlockAsLayoutChild", "Slackblocks.Models.cardBlockBlockId", "Slackblocks.Models.cardBlockBody", "Slackblocks.Models.cardBlockExtensions",@@ -412,6 +438,7 @@ "Slackblocks.Models.cardBlockTitle", "Slackblocks.Models.carouselBlock", "Slackblocks.Models.carouselBlockAsBlock",+ "Slackblocks.Models.carouselBlockAsLayoutChild", "Slackblocks.Models.carouselBlockBlockId", "Slackblocks.Models.carouselBlockElements", "Slackblocks.Models.carouselBlockExtensions",@@ -461,6 +488,7 @@ "Slackblocks.Models.confirmationDialogueTitle", "Slackblocks.Models.containerBlock", "Slackblocks.Models.containerBlockAsBlock",+ "Slackblocks.Models.containerBlockAsLayoutChild", "Slackblocks.Models.containerBlockBlockId", "Slackblocks.Models.containerBlockChildBlocks", "Slackblocks.Models.containerBlockDefaultCollapsed",@@ -479,6 +507,8 @@ "Slackblocks.Models.contextActionsBlockExtensions", "Slackblocks.Models.contextBlock", "Slackblocks.Models.contextBlockAsBlock",+ "Slackblocks.Models.contextBlockAsLayoutChild",+ "Slackblocks.Models.contextBlockAsLayoutFallback", "Slackblocks.Models.contextBlockBlockId", "Slackblocks.Models.contextBlockElements", "Slackblocks.Models.contextBlockExtensions",@@ -521,6 +551,7 @@ "Slackblocks.Models.dataSeriesName", "Slackblocks.Models.dataTableBlock", "Slackblocks.Models.dataTableBlockAsBlock",+ "Slackblocks.Models.dataTableBlockAsLayoutChild", "Slackblocks.Models.dataTableBlockBlockId", "Slackblocks.Models.dataTableBlockCaption", "Slackblocks.Models.dataTableBlockExtensions",@@ -529,6 +560,7 @@ "Slackblocks.Models.dataTableBlockRows", "Slackblocks.Models.dataVisualizationBlock", "Slackblocks.Models.dataVisualizationBlockAsBlock",+ "Slackblocks.Models.dataVisualizationBlockAsLayoutChild", "Slackblocks.Models.dataVisualizationBlockBlockId", "Slackblocks.Models.dataVisualizationBlockChart", "Slackblocks.Models.dataVisualizationBlockExtensions",@@ -555,6 +587,8 @@ "Slackblocks.Models.dispatchActionConfigurationTriggerActionsOn", "Slackblocks.Models.dividerBlock", "Slackblocks.Models.dividerBlockAsBlock",+ "Slackblocks.Models.dividerBlockAsLayoutChild",+ "Slackblocks.Models.dividerBlockAsLayoutFallback", "Slackblocks.Models.dividerBlockBlockId", "Slackblocks.Models.dividerBlockExtensions", "Slackblocks.Models.emailInputElement",@@ -601,6 +635,7 @@ "Slackblocks.Models.feedbackButtonsElementPositiveButton", "Slackblocks.Models.fileBlock", "Slackblocks.Models.fileBlockAsBlock",+ "Slackblocks.Models.fileBlockAsLayoutChild", "Slackblocks.Models.fileBlockBlockId", "Slackblocks.Models.fileBlockExtensions", "Slackblocks.Models.fileBlockExternalId",@@ -614,6 +649,8 @@ "Slackblocks.Models.fileInputElementMaxFiles", "Slackblocks.Models.headerBlock", "Slackblocks.Models.headerBlockAsBlock",+ "Slackblocks.Models.headerBlockAsLayoutChild",+ "Slackblocks.Models.headerBlockAsLayoutFallback", "Slackblocks.Models.headerBlockBlockId", "Slackblocks.Models.headerBlockExtensions", "Slackblocks.Models.headerBlockText",@@ -637,6 +674,8 @@ "Slackblocks.Models.imageBlock", "Slackblocks.Models.imageBlockAltText", "Slackblocks.Models.imageBlockAsBlock",+ "Slackblocks.Models.imageBlockAsLayoutChild",+ "Slackblocks.Models.imageBlockAsLayoutFallback", "Slackblocks.Models.imageBlockBlockId", "Slackblocks.Models.imageBlockExtensions", "Slackblocks.Models.imageBlockImageUrl",@@ -651,6 +690,7 @@ "Slackblocks.Models.imageElementSlackFile", "Slackblocks.Models.inputBlock", "Slackblocks.Models.inputBlockAsBlock",+ "Slackblocks.Models.inputBlockAsLayoutChild", "Slackblocks.Models.inputBlockBlockId", "Slackblocks.Models.inputBlockDispatchAction", "Slackblocks.Models.inputBlockElement",@@ -705,6 +745,9 @@ "Slackblocks.Models.inspectImageElement", "Slackblocks.Models.inspectInputBlock", "Slackblocks.Models.inspectInputParameter",+ "Slackblocks.Models.inspectLayoutBlock",+ "Slackblocks.Models.inspectLayoutItem",+ "Slackblocks.Models.inspectLayoutRow", "Slackblocks.Models.inspectLineChart", "Slackblocks.Models.inspectMarkdownBlock", "Slackblocks.Models.inspectMarkdownText",@@ -752,6 +795,23 @@ "Slackblocks.Models.inspectWebhookMessage", "Slackblocks.Models.inspectWorkflow", "Slackblocks.Models.inspectWorkflowButtonElement",+ "Slackblocks.Models.layoutBlock",+ "Slackblocks.Models.layoutBlockAsBlock",+ "Slackblocks.Models.layoutBlockBlockId",+ "Slackblocks.Models.layoutBlockExtensions",+ "Slackblocks.Models.layoutBlockFallback",+ "Slackblocks.Models.layoutBlockGridLines",+ "Slackblocks.Models.layoutBlockIsLabelVisible",+ "Slackblocks.Models.layoutBlockLabel",+ "Slackblocks.Models.layoutBlockRows",+ "Slackblocks.Models.layoutItem",+ "Slackblocks.Models.layoutItemBlock",+ "Slackblocks.Models.layoutItemExtensions",+ "Slackblocks.Models.layoutItemHorizontalAlignment",+ "Slackblocks.Models.layoutItemWidth",+ "Slackblocks.Models.layoutRow",+ "Slackblocks.Models.layoutRowExtensions",+ "Slackblocks.Models.layoutRowItems", "Slackblocks.Models.lineChart", "Slackblocks.Models.lineChartAsChart", "Slackblocks.Models.lineChartAxisConfig",@@ -782,6 +842,7 @@ "Slackblocks.Models.matchBlockHeaderBlock", "Slackblocks.Models.matchBlockImageBlock", "Slackblocks.Models.matchBlockInputBlock",+ "Slackblocks.Models.matchBlockLayoutBlock", "Slackblocks.Models.matchBlockMarkdownBlock", "Slackblocks.Models.matchBlockPlanBlock", "Slackblocks.Models.matchBlockRichTextBlock",@@ -849,6 +910,31 @@ "Slackblocks.Models.matchInputElementUrlInputElement", "Slackblocks.Models.matchInputElementUserMultiSelectElement", "Slackblocks.Models.matchInputElementUserSelectElement",+ "Slackblocks.Models.matchLayoutChildActionsBlock",+ "Slackblocks.Models.matchLayoutChildAlertBlock",+ "Slackblocks.Models.matchLayoutChildCardBlock",+ "Slackblocks.Models.matchLayoutChildCarouselBlock",+ "Slackblocks.Models.matchLayoutChildContainerBlock",+ "Slackblocks.Models.matchLayoutChildContextBlock",+ "Slackblocks.Models.matchLayoutChildDataTableBlock",+ "Slackblocks.Models.matchLayoutChildDataVisualizationBlock",+ "Slackblocks.Models.matchLayoutChildDividerBlock",+ "Slackblocks.Models.matchLayoutChildFileBlock",+ "Slackblocks.Models.matchLayoutChildHeaderBlock",+ "Slackblocks.Models.matchLayoutChildImageBlock",+ "Slackblocks.Models.matchLayoutChildInputBlock",+ "Slackblocks.Models.matchLayoutChildRichTextBlock",+ "Slackblocks.Models.matchLayoutChildSectionBlock",+ "Slackblocks.Models.matchLayoutChildTableBlock",+ "Slackblocks.Models.matchLayoutChildVideoBlock",+ "Slackblocks.Models.matchLayoutFallbackContextBlock",+ "Slackblocks.Models.matchLayoutFallbackDividerBlock",+ "Slackblocks.Models.matchLayoutFallbackHeaderBlock",+ "Slackblocks.Models.matchLayoutFallbackImageBlock",+ "Slackblocks.Models.matchLayoutFallbackRichTextBlock",+ "Slackblocks.Models.matchLayoutFallbackSectionBlock",+ "Slackblocks.Models.matchLayoutFallbackTableBlock",+ "Slackblocks.Models.matchLayoutFallbackVideoBlock", "Slackblocks.Models.matchRichTextBlockElementRichTextCodeBlock", "Slackblocks.Models.matchRichTextBlockElementRichTextList", "Slackblocks.Models.matchRichTextBlockElementRichTextQuote",@@ -962,6 +1048,8 @@ "Slackblocks.Models.richTextBlock", "Slackblocks.Models.richTextBlockAsBlock", "Slackblocks.Models.richTextBlockAsDataTableCell",+ "Slackblocks.Models.richTextBlockAsLayoutChild",+ "Slackblocks.Models.richTextBlockAsLayoutFallback", "Slackblocks.Models.richTextBlockAsTableCell", "Slackblocks.Models.richTextBlockBlockId", "Slackblocks.Models.richTextBlockElements",@@ -1034,6 +1122,8 @@ "Slackblocks.Models.sectionBlock", "Slackblocks.Models.sectionBlockAccessory", "Slackblocks.Models.sectionBlockAsBlock",+ "Slackblocks.Models.sectionBlockAsLayoutChild",+ "Slackblocks.Models.sectionBlockAsLayoutFallback", "Slackblocks.Models.sectionBlockBlockId", "Slackblocks.Models.sectionBlockExtensions", "Slackblocks.Models.sectionBlockFields",@@ -1081,6 +1171,8 @@ "Slackblocks.Models.staticSelectElementPlaceholder", "Slackblocks.Models.tableBlock", "Slackblocks.Models.tableBlockAsBlock",+ "Slackblocks.Models.tableBlockAsLayoutChild",+ "Slackblocks.Models.tableBlockAsLayoutFallback", "Slackblocks.Models.tableBlockBlockId", "Slackblocks.Models.tableBlockColumnSettings", "Slackblocks.Models.tableBlockExtensions",@@ -1144,6 +1236,8 @@ "Slackblocks.Models.videoBlock", "Slackblocks.Models.videoBlockAltText", "Slackblocks.Models.videoBlockAsBlock",+ "Slackblocks.Models.videoBlockAsLayoutChild",+ "Slackblocks.Models.videoBlockAsLayoutFallback", "Slackblocks.Models.videoBlockAuthorName", "Slackblocks.Models.videoBlockBlockId", "Slackblocks.Models.videoBlockDescription",@@ -1345,6 +1439,26 @@ "Slackblocks.Models.InputElement", "Slackblocks.Models.InputParameter", "Slackblocks.Models.InputParameterFields",+ "Slackblocks.Models.LayoutAlignment",+ "Slackblocks.Models.LayoutAlignmentCenter",+ "Slackblocks.Models.LayoutAlignmentEnd",+ "Slackblocks.Models.LayoutAlignmentStart",+ "Slackblocks.Models.LayoutBlock",+ "Slackblocks.Models.LayoutBlockFields",+ "Slackblocks.Models.LayoutChild",+ "Slackblocks.Models.LayoutFallback",+ "Slackblocks.Models.LayoutGridLines",+ "Slackblocks.Models.LayoutGridLinesNone",+ "Slackblocks.Models.LayoutGridLinesOuter",+ "Slackblocks.Models.LayoutGridLinesRows",+ "Slackblocks.Models.LayoutItem",+ "Slackblocks.Models.LayoutItemFields",+ "Slackblocks.Models.LayoutRow",+ "Slackblocks.Models.LayoutRowFields",+ "Slackblocks.Models.LayoutWidth",+ "Slackblocks.Models.LayoutWidthNarrow",+ "Slackblocks.Models.LayoutWidthStandard",+ "Slackblocks.Models.LayoutWidthWide", "Slackblocks.Models.LineChart", "Slackblocks.Models.LineChartFields", "Slackblocks.Models.MarkdownBlock",@@ -1455,11 +1569,13 @@ "Slackblocks.Models.WorkflowFields", "Slackblocks.Models.actionsBlock", "Slackblocks.Models.actionsBlockAsBlock",+ "Slackblocks.Models.actionsBlockAsLayoutChild", "Slackblocks.Models.actionsBlockBlockId", "Slackblocks.Models.actionsBlockElements", "Slackblocks.Models.actionsBlockExtensions", "Slackblocks.Models.alertBlock", "Slackblocks.Models.alertBlockAsBlock",+ "Slackblocks.Models.alertBlockAsLayoutChild", "Slackblocks.Models.alertBlockBlockId", "Slackblocks.Models.alertBlockExtensions", "Slackblocks.Models.alertBlockLevel",@@ -1527,6 +1643,9 @@ "Slackblocks.Models.buildImageElement", "Slackblocks.Models.buildInputBlock", "Slackblocks.Models.buildInputParameter",+ "Slackblocks.Models.buildLayoutBlock",+ "Slackblocks.Models.buildLayoutItem",+ "Slackblocks.Models.buildLayoutRow", "Slackblocks.Models.buildLineChart", "Slackblocks.Models.buildMarkdownBlock", "Slackblocks.Models.buildMarkdownText",@@ -1589,6 +1708,7 @@ "Slackblocks.Models.cardBlock", "Slackblocks.Models.cardBlockActions", "Slackblocks.Models.cardBlockAsBlock",+ "Slackblocks.Models.cardBlockAsLayoutChild", "Slackblocks.Models.cardBlockBlockId", "Slackblocks.Models.cardBlockBody", "Slackblocks.Models.cardBlockExtensions",@@ -1600,6 +1720,7 @@ "Slackblocks.Models.cardBlockTitle", "Slackblocks.Models.carouselBlock", "Slackblocks.Models.carouselBlockAsBlock",+ "Slackblocks.Models.carouselBlockAsLayoutChild", "Slackblocks.Models.carouselBlockBlockId", "Slackblocks.Models.carouselBlockElements", "Slackblocks.Models.carouselBlockExtensions",@@ -1649,6 +1770,7 @@ "Slackblocks.Models.confirmationDialogueTitle", "Slackblocks.Models.containerBlock", "Slackblocks.Models.containerBlockAsBlock",+ "Slackblocks.Models.containerBlockAsLayoutChild", "Slackblocks.Models.containerBlockBlockId", "Slackblocks.Models.containerBlockChildBlocks", "Slackblocks.Models.containerBlockDefaultCollapsed",@@ -1667,6 +1789,8 @@ "Slackblocks.Models.contextActionsBlockExtensions", "Slackblocks.Models.contextBlock", "Slackblocks.Models.contextBlockAsBlock",+ "Slackblocks.Models.contextBlockAsLayoutChild",+ "Slackblocks.Models.contextBlockAsLayoutFallback", "Slackblocks.Models.contextBlockBlockId", "Slackblocks.Models.contextBlockElements", "Slackblocks.Models.contextBlockExtensions",@@ -1709,6 +1833,7 @@ "Slackblocks.Models.dataSeriesName", "Slackblocks.Models.dataTableBlock", "Slackblocks.Models.dataTableBlockAsBlock",+ "Slackblocks.Models.dataTableBlockAsLayoutChild", "Slackblocks.Models.dataTableBlockBlockId", "Slackblocks.Models.dataTableBlockCaption", "Slackblocks.Models.dataTableBlockExtensions",@@ -1717,6 +1842,7 @@ "Slackblocks.Models.dataTableBlockRows", "Slackblocks.Models.dataVisualizationBlock", "Slackblocks.Models.dataVisualizationBlockAsBlock",+ "Slackblocks.Models.dataVisualizationBlockAsLayoutChild", "Slackblocks.Models.dataVisualizationBlockBlockId", "Slackblocks.Models.dataVisualizationBlockChart", "Slackblocks.Models.dataVisualizationBlockExtensions",@@ -1743,6 +1869,8 @@ "Slackblocks.Models.dispatchActionConfigurationTriggerActionsOn", "Slackblocks.Models.dividerBlock", "Slackblocks.Models.dividerBlockAsBlock",+ "Slackblocks.Models.dividerBlockAsLayoutChild",+ "Slackblocks.Models.dividerBlockAsLayoutFallback", "Slackblocks.Models.dividerBlockBlockId", "Slackblocks.Models.dividerBlockExtensions", "Slackblocks.Models.emailInputElement",@@ -1789,6 +1917,7 @@ "Slackblocks.Models.feedbackButtonsElementPositiveButton", "Slackblocks.Models.fileBlock", "Slackblocks.Models.fileBlockAsBlock",+ "Slackblocks.Models.fileBlockAsLayoutChild", "Slackblocks.Models.fileBlockBlockId", "Slackblocks.Models.fileBlockExtensions", "Slackblocks.Models.fileBlockExternalId",@@ -1802,6 +1931,8 @@ "Slackblocks.Models.fileInputElementMaxFiles", "Slackblocks.Models.headerBlock", "Slackblocks.Models.headerBlockAsBlock",+ "Slackblocks.Models.headerBlockAsLayoutChild",+ "Slackblocks.Models.headerBlockAsLayoutFallback", "Slackblocks.Models.headerBlockBlockId", "Slackblocks.Models.headerBlockExtensions", "Slackblocks.Models.headerBlockText",@@ -1825,6 +1956,8 @@ "Slackblocks.Models.imageBlock", "Slackblocks.Models.imageBlockAltText", "Slackblocks.Models.imageBlockAsBlock",+ "Slackblocks.Models.imageBlockAsLayoutChild",+ "Slackblocks.Models.imageBlockAsLayoutFallback", "Slackblocks.Models.imageBlockBlockId", "Slackblocks.Models.imageBlockExtensions", "Slackblocks.Models.imageBlockImageUrl",@@ -1839,6 +1972,7 @@ "Slackblocks.Models.imageElementSlackFile", "Slackblocks.Models.inputBlock", "Slackblocks.Models.inputBlockAsBlock",+ "Slackblocks.Models.inputBlockAsLayoutChild", "Slackblocks.Models.inputBlockBlockId", "Slackblocks.Models.inputBlockDispatchAction", "Slackblocks.Models.inputBlockElement",@@ -1893,6 +2027,9 @@ "Slackblocks.Models.inspectImageElement", "Slackblocks.Models.inspectInputBlock", "Slackblocks.Models.inspectInputParameter",+ "Slackblocks.Models.inspectLayoutBlock",+ "Slackblocks.Models.inspectLayoutItem",+ "Slackblocks.Models.inspectLayoutRow", "Slackblocks.Models.inspectLineChart", "Slackblocks.Models.inspectMarkdownBlock", "Slackblocks.Models.inspectMarkdownText",@@ -1940,6 +2077,23 @@ "Slackblocks.Models.inspectWebhookMessage", "Slackblocks.Models.inspectWorkflow", "Slackblocks.Models.inspectWorkflowButtonElement",+ "Slackblocks.Models.layoutBlock",+ "Slackblocks.Models.layoutBlockAsBlock",+ "Slackblocks.Models.layoutBlockBlockId",+ "Slackblocks.Models.layoutBlockExtensions",+ "Slackblocks.Models.layoutBlockFallback",+ "Slackblocks.Models.layoutBlockGridLines",+ "Slackblocks.Models.layoutBlockIsLabelVisible",+ "Slackblocks.Models.layoutBlockLabel",+ "Slackblocks.Models.layoutBlockRows",+ "Slackblocks.Models.layoutItem",+ "Slackblocks.Models.layoutItemBlock",+ "Slackblocks.Models.layoutItemExtensions",+ "Slackblocks.Models.layoutItemHorizontalAlignment",+ "Slackblocks.Models.layoutItemWidth",+ "Slackblocks.Models.layoutRow",+ "Slackblocks.Models.layoutRowExtensions",+ "Slackblocks.Models.layoutRowItems", "Slackblocks.Models.lineChart", "Slackblocks.Models.lineChartAsChart", "Slackblocks.Models.lineChartAxisConfig",@@ -1970,6 +2124,7 @@ "Slackblocks.Models.matchBlockHeaderBlock", "Slackblocks.Models.matchBlockImageBlock", "Slackblocks.Models.matchBlockInputBlock",+ "Slackblocks.Models.matchBlockLayoutBlock", "Slackblocks.Models.matchBlockMarkdownBlock", "Slackblocks.Models.matchBlockPlanBlock", "Slackblocks.Models.matchBlockRichTextBlock",@@ -2037,6 +2192,31 @@ "Slackblocks.Models.matchInputElementUrlInputElement", "Slackblocks.Models.matchInputElementUserMultiSelectElement", "Slackblocks.Models.matchInputElementUserSelectElement",+ "Slackblocks.Models.matchLayoutChildActionsBlock",+ "Slackblocks.Models.matchLayoutChildAlertBlock",+ "Slackblocks.Models.matchLayoutChildCardBlock",+ "Slackblocks.Models.matchLayoutChildCarouselBlock",+ "Slackblocks.Models.matchLayoutChildContainerBlock",+ "Slackblocks.Models.matchLayoutChildContextBlock",+ "Slackblocks.Models.matchLayoutChildDataTableBlock",+ "Slackblocks.Models.matchLayoutChildDataVisualizationBlock",+ "Slackblocks.Models.matchLayoutChildDividerBlock",+ "Slackblocks.Models.matchLayoutChildFileBlock",+ "Slackblocks.Models.matchLayoutChildHeaderBlock",+ "Slackblocks.Models.matchLayoutChildImageBlock",+ "Slackblocks.Models.matchLayoutChildInputBlock",+ "Slackblocks.Models.matchLayoutChildRichTextBlock",+ "Slackblocks.Models.matchLayoutChildSectionBlock",+ "Slackblocks.Models.matchLayoutChildTableBlock",+ "Slackblocks.Models.matchLayoutChildVideoBlock",+ "Slackblocks.Models.matchLayoutFallbackContextBlock",+ "Slackblocks.Models.matchLayoutFallbackDividerBlock",+ "Slackblocks.Models.matchLayoutFallbackHeaderBlock",+ "Slackblocks.Models.matchLayoutFallbackImageBlock",+ "Slackblocks.Models.matchLayoutFallbackRichTextBlock",+ "Slackblocks.Models.matchLayoutFallbackSectionBlock",+ "Slackblocks.Models.matchLayoutFallbackTableBlock",+ "Slackblocks.Models.matchLayoutFallbackVideoBlock", "Slackblocks.Models.matchRichTextBlockElementRichTextCodeBlock", "Slackblocks.Models.matchRichTextBlockElementRichTextList", "Slackblocks.Models.matchRichTextBlockElementRichTextQuote",@@ -2150,6 +2330,8 @@ "Slackblocks.Models.richTextBlock", "Slackblocks.Models.richTextBlockAsBlock", "Slackblocks.Models.richTextBlockAsDataTableCell",+ "Slackblocks.Models.richTextBlockAsLayoutChild",+ "Slackblocks.Models.richTextBlockAsLayoutFallback", "Slackblocks.Models.richTextBlockAsTableCell", "Slackblocks.Models.richTextBlockBlockId", "Slackblocks.Models.richTextBlockElements",@@ -2222,6 +2404,8 @@ "Slackblocks.Models.sectionBlock", "Slackblocks.Models.sectionBlockAccessory", "Slackblocks.Models.sectionBlockAsBlock",+ "Slackblocks.Models.sectionBlockAsLayoutChild",+ "Slackblocks.Models.sectionBlockAsLayoutFallback", "Slackblocks.Models.sectionBlockBlockId", "Slackblocks.Models.sectionBlockExtensions", "Slackblocks.Models.sectionBlockFields",@@ -2269,6 +2453,8 @@ "Slackblocks.Models.staticSelectElementPlaceholder", "Slackblocks.Models.tableBlock", "Slackblocks.Models.tableBlockAsBlock",+ "Slackblocks.Models.tableBlockAsLayoutChild",+ "Slackblocks.Models.tableBlockAsLayoutFallback", "Slackblocks.Models.tableBlockBlockId", "Slackblocks.Models.tableBlockColumnSettings", "Slackblocks.Models.tableBlockExtensions",@@ -2332,6 +2518,8 @@ "Slackblocks.Models.videoBlock", "Slackblocks.Models.videoBlockAltText", "Slackblocks.Models.videoBlockAsBlock",+ "Slackblocks.Models.videoBlockAsLayoutChild",+ "Slackblocks.Models.videoBlockAsLayoutFallback", "Slackblocks.Models.videoBlockAuthorName", "Slackblocks.Models.videoBlockBlockId", "Slackblocks.Models.videoBlockDescription",@@ -2781,6 +2969,18 @@ "WorkflowButtonElement": { "type": "Slackblocks.Models.WorkflowButtonElement", "capability": "elements.workflow_button"+ },+ "LayoutBlock": {+ "type": "Slackblocks.Models.LayoutBlock",+ "capability": "blocks.layout"+ },+ "LayoutRow": {+ "type": "Slackblocks.Models.LayoutRow",+ "capability": "objects.layout_row"+ },+ "LayoutItem": {+ "type": "Slackblocks.Models.LayoutItem",+ "capability": "objects.layout_item" } }, "supporting_api": [@@ -2824,6 +3024,15 @@ "Boundaries.hs", "bin/check compiler controls" ]+ },+ {+ "reason": "Closed LayoutChild/LayoutFallback conversions and distinct layout widths, alignments and grid lines preserve native roles and wire values.",+ "tests": [+ "Foundation.hs",+ "Constructions.hs",+ "bin/check layout compile-fail controls"+ ],+ "api": "Slackblocks.Models layout roles/enums" } ] }
generator/generate.py view
@@ -191,7 +191,7 @@ 'import Data.Aeson (FromJSON(..), ToJSON(..), Value(..), object, (.=))','import Data.Map.Strict (Map)','import qualified Data.Map.Strict as Map', 'import Data.Scientific (Scientific)','import Data.Text (Text)','import Slackblocks.Validation','import Slackblocks.Json (FromSlackJSON(..))','import Slackblocks.Internal.Wire', 'import Slackblocks.Internal.Rules (validateFields, checkIntegerBounds, standaloneTask)','import Slackblocks.Style',- '-- | Coordinated package version, distinct from the shared contract.', 'packageVersion :: Text', '-- | Shared Block Kit contract version implemented by this package.', 'specVersion :: Text','packageVersion = "3.1.0"', 'specVersion = '+string(json.loads((ROOT/'spec/manifest.json').read_text(encoding="utf-8"))['spec_version']),'']+ '-- | Coordinated package version, distinct from the shared contract.', 'packageVersion :: Text', '-- | Shared Block Kit contract version implemented by this package.', 'specVersion :: Text','packageVersion = "3.2.0"', 'specVersion = '+string(json.loads((ROOT/'spec/manifest.json').read_text(encoding="utf-8"))['spec_version']),''] return '\n'.join(header+lines)+'\n'
integrations/transport.cabal view
@@ -14,7 +14,7 @@ ghc-options: -Wall -Wcompat build-depends: base >=4.18 && <4.23,- slackblocks ==3.1.0,+ slackblocks ==3.2.0, aeson >=2.2 && <2.4, bytestring >=0.11.5.4 && <0.13, containers >=0.6.7 && <0.9,
slackblocks.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: slackblocks-version: 3.1.0+version: 3.2.0 synopsis: Typed and validated Slack Block Kit composition description: Immutable Block Kit values, checked construction, and Aeson encoding. homepage: https://github.com/nicklambourne/slackblocks
src/Slackblocks/Internal/Rules.hs view
@@ -111,7 +111,17 @@ "CardBlock" -> do check MissingRequired [] "card.content" (any present ["hero_image", "title", "actions", "body"]) check MutuallyExclusive [] "card.icon" (not (present "icon" && present "slack_icon"))+ "LayoutBlock" -> compositeDepth value 0 False+ "LayoutItem" -> allowedKeys value ["block", "width", "horizontal_alignment"]+ "LayoutRow" -> do+ allowedKeys value ["items"]+ let items = array (get "items")+ wide = filter ((`elem` [String "carousel", String "data_table", String "data_visualization"]) . at "type" . at "block") items+ total = sum (map layoutWeight items)+ check InvalidUsage [] "layout.row.wide_blocks" (length wide <= 1)+ mapM_ (\item -> when (at "type" (at "block" item) == String "data_visualization") (check InvalidUsage [] "layout.row.chart_width" (layoutWeight item * 2 > total))) items "ContainerBlock" -> do+ compositeDepth value 0 False check MissingRequired [] "container.title" (present "title" || present "rich_text_title") check InvalidUsage [Field "default_collapsed"] "container.default_collapsed" (get "default_collapsed" /= Bool True || get "is_collapsible" == Bool True) check InvalidUsage [Field "has_header_divider"] "container.has_header_divider" (get "has_header_divider" /= Bool True || get "is_collapsible" /= Bool True)@@ -154,7 +164,7 @@ "WebhookMessage" -> message value "ModalView" -> do surface "modal" [Field "blocks"] (array (get "blocks"))- check MissingRequired [Field "submit"] "view.submit.required" (present "submit" || not (any ((== String "input") . at "type") (array (get "blocks"))))+ check MissingRequired [Field "submit"] "view.submit.required" (present "submit" || not (any (any ((== String "input") . at "type") . descendants) (array (get "blocks")))) "HomeTabView" -> surface "home" [Field "blocks"] (array (get "blocks")) _ -> Right () where@@ -184,18 +194,40 @@ | otherwise = Right () surface :: Text -> [PathPiece] -> [Value] -> Result ()-surface name path =+surface name path blocks = do mapM_ ( \(i, block) -> do+ surface name (path <> [Index i, Field "children"]) (compositeChildren False block)+ when (at "type" block == String "layout") (surface name (path <> [Index i, Field "fallback"]) (array (at "fallback" block))) check TypeMismatch (path <> [Index i, Field "type"]) "surface_block_types" (text (at "type" block) `elem` surfaceBlocks name) either (Left . fmap (\e -> e {errorPath = path <> [Index i] <> errorPath e})) Right (standaloneTask block) )- . zip [0 ..]+ (zip [0 ..] blocks) -- Traverse only modeled nested blocks. Metadata values with a "type" key do -- not count as blocks, charts, markdown or tables. descendants :: Value -> [Value]-descendants block = block : if at "type" block == String "container" then concatMap descendants (array (at "child_blocks" block)) else []+descendants block = block : concatMap descendants (compositeChildren True block)++layoutWeight :: Value -> Int+layoutWeight item = case at "width" item of String "narrow" -> 1; String "wide" -> 3; _ -> 2++allowedKeys :: Value -> [Text] -> Result ()+allowedKeys (Object fields) keys = mapM_ (\key -> check TypeMismatch [Field (K.toText key)] "layout.properties" (K.toText key `elem` keys)) (KM.keys fields)+allowedKeys _ _ = Right ()++compositeChildren :: Bool -> Value -> [Value]+compositeChildren includeFallback block = case at "type" block of+ String "container" -> array (at "child_blocks" block)+ String "layout" -> [at "block" item | row <- array (at "rows" block), item <- array (at "items" row)] <> if includeFallback then array (at "fallback" block) else []+ _ -> []++compositeDepth :: Value -> Int -> Bool -> Result ()+compositeDepth block depth seen = do+ let hasLayout = seen || at "type" block == String "layout"+ next = depth + if at "type" block `elem` [String "layout", String "container"] then 1 else 0+ check InvalidUsage [] "layout.nesting" (not hasLayout || next <= 2)+ mapM_ (\child -> compositeDepth child next hasLayout) (compositeChildren False block) message :: Value -> Result () message value = do
src/Slackblocks/Internal/Schema.hs view
@@ -94,17 +94,20 @@ "MessageResponse" -> [("blocks", "list", "message.blocks", [("max_items", 50)], []), ("attachments", "list", "message.attachments", [("max_items", 100)], [])] "ModalView" -> [("title", "text", "view.title", [("max_length", 24)], []), ("blocks", "list", "view.blocks", [("max_items", 100)], []), ("close", "text", "view.close", [("max_length", 24)], []), ("submit", "text", "view.submit", [("max_length", 24)], []), ("private_metadata", "string", "view.private_metadata", [("max_length", 3000)], []), ("callback_id", "string", "view.callback_id", [("max_length", 255)], []), ("external_id", "string", "view.external_id", [("max_length", 255)], [])] "WebhookMessage" -> [("blocks", "list", "message.blocks", [("max_items", 50)], []), ("attachments", "list", "message.attachments", [("max_items", 100)], [])]+ "LayoutBlock" -> [("label", "string", "layout.label", [("min_length", 1), ("max_length", 150)], []), ("rows", "list", "layout.rows", [("min_items", 1), ("max_items", 5)], []), ("block_id", "string", "block_id", [("max_length", 255)], [])]+ "LayoutRow" -> [("items", "list", "layout.items", [("min_items", 1), ("max_items", 3)], [])]+ "LayoutItem" -> [] _ -> [] limits :: [(Text, Integer)]-limits = [("text.min_length", 1), ("text.max_length", 3000), ("action_id.max_length", 255), ("block_id.max_length", 255), ("button.text.max_length", 75), ("button.url.max_length", 3000), ("button.value.max_length", 2000), ("button.accessibility_label.max_length", 75), ("button.agent_prompt.max_length", 4000), ("confirmation.title.max_length", 100), ("confirmation.text.max_length", 300), ("confirmation.confirm.max_length", 30), ("confirmation.deny.max_length", 30), ("option.text.max_length", 75), ("option.value.max_length", 150), ("option.description.max_length", 75), ("option.url.max_length", 3000), ("option_group.label.max_length", 75), ("option_group.options.min_items", 1), ("option_group.options.max_items", 100), ("select.placeholder.max_length", 150), ("select.options.min_items", 1), ("select.options.max_items", 100), ("select.option_groups.min_items", 1), ("select.option_groups.max_items", 100), ("select.min_query_length.min", 0), ("overflow.options.min_items", 1), ("overflow.options.max_items", 5), ("checkboxes.options.min_items", 1), ("checkboxes.options.max_items", 10), ("radio_buttons.options.min_items", 1), ("radio_buttons.options.max_items", 10), ("file_input.max_files.min", 1), ("file_input.max_files.max", 10), ("plain_text_input.min_length.min", 0), ("plain_text_input.min_length.max", 3000), ("plain_text_input.max_length.min", 1), ("plain_text_input.max_length.max", 3000), ("plain_text_input.placeholder.max_length", 150), ("email_input.placeholder.max_length", 150), ("url_input.placeholder.max_length", 150), ("number_input.placeholder.max_length", 150), ("date_picker.placeholder.max_length", 150), ("time_picker.placeholder.max_length", 150), ("rich_text_input.placeholder.max_length", 150), ("rich_text_input.min_lines.min", 1), ("rich_text_input.min_lines.max", 100), ("rich_text_input.max_lines.min", 1), ("rich_text_input.max_lines.max", 100), ("multi_select.max_selected_items.min", 1), ("conversation_filter.include.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.max_items", 2), ("actions.elements.max_items", 25), ("alert.text.max_length", 200), ("card.title.max_length", 150), ("card.subtitle.max_length", 150), ("card.body.max_length", 200), ("card.actions.max_items", 3), ("card.subtext.max_length", 200), ("carousel.elements.min_items", 1), ("carousel.elements.max_items", 10), ("container.title.max_length", 150), ("container.subtitle.max_length", 150), ("container.child_blocks.min_items", 1), ("container.child_blocks.max_items", 10), ("context.elements.max_items", 10), ("context_actions.elements.max_items", 5), ("feedback_button.text.max_length", 75), ("feedback_button.value.max_length", 2000), ("feedback_button.accessibility_label.max_length", 75), ("icon_button.value.max_length", 2000), ("icon_button.accessibility_label.max_length", 75), ("icon_button.visible_to_user_ids.max_items", 10), ("workflow_button.text.max_length", 75), ("workflow_button.accessibility_label.max_length", 75), ("table.rows.max_items", 100), ("table.columns.max_items", 20), ("table.column_settings.max_items", 20), ("data_table.rows.min_items", 2), ("data_table.rows.max_items", 201), ("data_table.columns.min_items", 1), ("data_table.columns.max_items", 20), ("data_table.page_size.min", 1), ("data_table.page_size.max", 100), ("data_table.cell_text.min_length", 1), ("data_table.content.max_length", 20000), ("data_table.total_content.max_length", 20000), ("data_table.row_header_column_index.min", 0), ("data_visualization.title.max_length", 50), ("data_visualization.segments.min_items", 1), ("data_visualization.segments.max_items", 12), ("data_visualization.segment.label.max_length", 20), ("data_visualization.segment.value.exclusive_min", 0), ("data_visualization.series.min_items", 1), ("data_visualization.series.max_items", 12), ("data_visualization.series_name.max_length", 20), ("data_visualization.data.min_items", 1), ("data_visualization.data.max_items", 20), ("data_visualization.point_label.max_length", 20), ("data_visualization.categories.min_items", 1), ("data_visualization.categories.max_items", 20), ("data_visualization.category_label.max_length", 20), ("data_visualization.axis_label.max_length", 50), ("header.text.max_length", 150), ("image.image_url.max_length", 3000), ("image.alt_text.max_length", 2000), ("image.title.max_length", 2000), ("image_element.image_url.max_length", 3000), ("image_element.alt_text.max_length", 2000), ("input.label.max_length", 2000), ("input.hint.max_length", 2000), ("markdown.text.max_length", 12000), ("markdown.total_text.max_length", 12000), ("plan.tasks.max_items", 50), ("rich_text_list.indent.min", 0), ("rich_text_list.indent.max", 8), ("rich_text_list.offset.min", 0), ("rich_text_list.border.min", 0), ("rich_text_list.border.max", 1), ("rich_text_quote.border.min", 0), ("rich_text_quote.border.max", 1), ("rich_text_preformatted.border.min", 0), ("rich_text_preformatted.border.max", 1), ("section.text.max_length", 3000), ("section.fields.max_items", 10), ("section.fields.item_max_length", 2000), ("video.alt_text.max_length", 2000), ("video.title.max_length", 200), ("video.author_name.max_length", 50), ("video.description.max_length", 200), ("video.provider_name.max_length", 50), ("video.thumbnail_url.max_length", 3000), ("video.video_url.max_length", 3000), ("video.title_url.max_length", 2000), ("video.provider_icon_url.max_length", 2000), ("message.data_visualizations.max_items", 2), ("message.channel.min_length", 1), ("message.blocks.max_items", 50), ("message.attachments.max_items", 100), ("view.blocks.max_items", 100), ("view.private_metadata.max_length", 3000), ("view.callback_id.max_length", 255), ("view.external_id.max_length", 255), ("view.title.max_length", 24), ("view.close.max_length", 24), ("view.submit.max_length", 24)]+limits = [("text.min_length", 1), ("text.max_length", 3000), ("action_id.max_length", 255), ("block_id.max_length", 255), ("button.text.max_length", 75), ("button.url.max_length", 3000), ("button.value.max_length", 2000), ("button.accessibility_label.max_length", 75), ("button.agent_prompt.max_length", 4000), ("confirmation.title.max_length", 100), ("confirmation.text.max_length", 300), ("confirmation.confirm.max_length", 30), ("confirmation.deny.max_length", 30), ("option.text.max_length", 75), ("option.value.max_length", 150), ("option.description.max_length", 75), ("option.url.max_length", 3000), ("option_group.label.max_length", 75), ("option_group.options.min_items", 1), ("option_group.options.max_items", 100), ("select.placeholder.max_length", 150), ("select.options.min_items", 1), ("select.options.max_items", 100), ("select.option_groups.min_items", 1), ("select.option_groups.max_items", 100), ("select.min_query_length.min", 0), ("overflow.options.min_items", 1), ("overflow.options.max_items", 5), ("checkboxes.options.min_items", 1), ("checkboxes.options.max_items", 10), ("radio_buttons.options.min_items", 1), ("radio_buttons.options.max_items", 10), ("file_input.max_files.min", 1), ("file_input.max_files.max", 10), ("plain_text_input.min_length.min", 0), ("plain_text_input.min_length.max", 3000), ("plain_text_input.max_length.min", 1), ("plain_text_input.max_length.max", 3000), ("plain_text_input.placeholder.max_length", 150), ("email_input.placeholder.max_length", 150), ("url_input.placeholder.max_length", 150), ("number_input.placeholder.max_length", 150), ("date_picker.placeholder.max_length", 150), ("time_picker.placeholder.max_length", 150), ("rich_text_input.placeholder.max_length", 150), ("rich_text_input.min_lines.min", 1), ("rich_text_input.min_lines.max", 100), ("rich_text_input.max_lines.min", 1), ("rich_text_input.max_lines.max", 100), ("multi_select.max_selected_items.min", 1), ("conversation_filter.include.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.max_items", 2), ("actions.elements.max_items", 25), ("alert.text.max_length", 200), ("card.title.max_length", 150), ("card.subtitle.max_length", 150), ("card.body.max_length", 200), ("card.actions.max_items", 3), ("card.subtext.max_length", 200), ("carousel.elements.min_items", 1), ("carousel.elements.max_items", 10), ("container.title.max_length", 150), ("container.subtitle.max_length", 150), ("container.child_blocks.min_items", 1), ("container.child_blocks.max_items", 10), ("context.elements.max_items", 10), ("context_actions.elements.max_items", 5), ("feedback_button.text.max_length", 75), ("feedback_button.value.max_length", 2000), ("feedback_button.accessibility_label.max_length", 75), ("icon_button.value.max_length", 2000), ("icon_button.accessibility_label.max_length", 75), ("icon_button.visible_to_user_ids.max_items", 10), ("workflow_button.text.max_length", 75), ("workflow_button.accessibility_label.max_length", 75), ("table.rows.max_items", 100), ("table.columns.max_items", 20), ("table.column_settings.max_items", 20), ("data_table.rows.min_items", 2), ("data_table.rows.max_items", 201), ("data_table.columns.min_items", 1), ("data_table.columns.max_items", 20), ("data_table.page_size.min", 1), ("data_table.page_size.max", 100), ("data_table.cell_text.min_length", 1), ("data_table.content.max_length", 20000), ("data_table.total_content.max_length", 20000), ("data_table.row_header_column_index.min", 0), ("data_visualization.title.max_length", 50), ("data_visualization.segments.min_items", 1), ("data_visualization.segments.max_items", 12), ("data_visualization.segment.label.max_length", 20), ("data_visualization.segment.value.exclusive_min", 0), ("data_visualization.series.min_items", 1), ("data_visualization.series.max_items", 12), ("data_visualization.series_name.max_length", 20), ("data_visualization.data.min_items", 1), ("data_visualization.data.max_items", 20), ("data_visualization.point_label.max_length", 20), ("data_visualization.categories.min_items", 1), ("data_visualization.categories.max_items", 20), ("data_visualization.category_label.max_length", 20), ("data_visualization.axis_label.max_length", 50), ("header.text.max_length", 150), ("image.image_url.max_length", 3000), ("image.alt_text.max_length", 2000), ("image.title.max_length", 2000), ("image_element.image_url.max_length", 3000), ("image_element.alt_text.max_length", 2000), ("input.label.max_length", 2000), ("input.hint.max_length", 2000), ("markdown.text.max_length", 12000), ("markdown.total_text.max_length", 12000), ("plan.tasks.max_items", 50), ("rich_text_list.indent.min", 0), ("rich_text_list.indent.max", 8), ("rich_text_list.offset.min", 0), ("rich_text_list.border.min", 0), ("rich_text_list.border.max", 1), ("rich_text_quote.border.min", 0), ("rich_text_quote.border.max", 1), ("rich_text_preformatted.border.min", 0), ("rich_text_preformatted.border.max", 1), ("section.text.max_length", 3000), ("section.fields.max_items", 10), ("section.fields.item_max_length", 2000), ("video.alt_text.max_length", 2000), ("video.title.max_length", 200), ("video.author_name.max_length", 50), ("video.description.max_length", 200), ("video.provider_name.max_length", 50), ("video.thumbnail_url.max_length", 3000), ("video.video_url.max_length", 3000), ("video.title_url.max_length", 2000), ("video.provider_icon_url.max_length", 2000), ("message.data_visualizations.max_items", 2), ("message.channel.min_length", 1), ("message.blocks.max_items", 50), ("message.attachments.max_items", 100), ("view.blocks.max_items", 100), ("view.private_metadata.max_length", 3000), ("view.callback_id.max_length", 255), ("view.external_id.max_length", 255), ("view.title.max_length", 24), ("view.close.max_length", 24), ("view.submit.max_length", 24), ("layout.label.min_length", 1), ("layout.label.max_length", 150), ("layout.rows.min_items", 1), ("layout.rows.max_items", 5), ("layout.items.min_items", 1), ("layout.items.max_items", 3)] icons :: [Text] icons = ["archive", "book", "bookmark", "bot", "bug", "calendar", "call", "caret-left", "caret-right", "check", "clipboard", "code", "comment", "compass", "copy", "cube", "download", "edit", "email", "eye-closed", "eye-open", "file", "flag", "folder", "gear", "globe", "heart", "help", "image", "info", "key", "lightbulb", "link", "map", "mobile", "new-window", "pin", "plus", "refine", "refresh", "rocket", "save", "screen", "share", "sparkle", "star", "star-filled", "tag", "thumbs-down", "thumbs-up", "trash", "upload", "user", "warning"] surfaceBlocks :: Text -> [Text] surfaceBlocks surface = case surface of- "home" -> ["actions", "card", "carousel", "container", "context", "data_table", "data_visualization", "divider", "header", "image", "input", "rich_text", "section", "table", "video"]- "message" -> ["actions", "card", "carousel", "container", "context", "context_actions", "data_table", "data_visualization", "divider", "file", "header", "image", "input", "markdown", "plan", "rich_text", "section", "table", "task_card", "video"]- "modal" -> ["actions", "alert", "card", "context", "divider", "header", "image", "input", "rich_text", "section", "video"]+ "home" -> ["actions", "card", "carousel", "container", "context", "data_table", "data_visualization", "divider", "header", "image", "input", "layout", "rich_text", "section", "table", "video"]+ "message" -> ["actions", "card", "carousel", "container", "context", "context_actions", "data_table", "data_visualization", "divider", "file", "header", "image", "input", "layout", "markdown", "plan", "rich_text", "section", "table", "task_card", "video"]+ "modal" -> ["actions", "alert", "card", "context", "divider", "header", "image", "input", "layout", "rich_text", "section", "video"] _ -> []
src/Slackblocks/Models.hs view
@@ -12,6 +12,9 @@ ColumnAlign (..), RichTextListStyle (..), ResponseType (..),+ LayoutWidth (..),+ LayoutAlignment (..),+ LayoutGridLines (..), ActionsBlock, ActionsBlockFields (..), actionsBlock,@@ -457,6 +460,21 @@ webhookMessage, buildWebhookMessage, inspectWebhookMessage,+ LayoutBlock,+ LayoutBlockFields (..),+ layoutBlock,+ buildLayoutBlock,+ inspectLayoutBlock,+ LayoutRow,+ LayoutRowFields (..),+ layoutRow,+ buildLayoutRow,+ inspectLayoutRow,+ LayoutItem,+ LayoutItemFields (..),+ layoutItem,+ buildLayoutItem,+ inspectLayoutItem, PlanTask, buildPlanTask, inspectPlanTask,@@ -503,6 +521,8 @@ matchBlockTaskCardBlock, videoBlockAsBlock, matchBlockVideoBlock,+ layoutBlockAsBlock,+ matchBlockLayoutBlock, ContextElement, imageElementAsContextElement, matchContextElementImageElement,@@ -661,6 +681,58 @@ matchRichTextSectionElementRichTextUser, richTextUserGroupAsRichTextSectionElement, matchRichTextSectionElementRichTextUserGroup,+ LayoutChild,+ actionsBlockAsLayoutChild,+ matchLayoutChildActionsBlock,+ alertBlockAsLayoutChild,+ matchLayoutChildAlertBlock,+ cardBlockAsLayoutChild,+ matchLayoutChildCardBlock,+ carouselBlockAsLayoutChild,+ matchLayoutChildCarouselBlock,+ containerBlockAsLayoutChild,+ matchLayoutChildContainerBlock,+ contextBlockAsLayoutChild,+ matchLayoutChildContextBlock,+ dataTableBlockAsLayoutChild,+ matchLayoutChildDataTableBlock,+ dataVisualizationBlockAsLayoutChild,+ matchLayoutChildDataVisualizationBlock,+ dividerBlockAsLayoutChild,+ matchLayoutChildDividerBlock,+ fileBlockAsLayoutChild,+ matchLayoutChildFileBlock,+ headerBlockAsLayoutChild,+ matchLayoutChildHeaderBlock,+ imageBlockAsLayoutChild,+ matchLayoutChildImageBlock,+ inputBlockAsLayoutChild,+ matchLayoutChildInputBlock,+ richTextBlockAsLayoutChild,+ matchLayoutChildRichTextBlock,+ sectionBlockAsLayoutChild,+ matchLayoutChildSectionBlock,+ tableBlockAsLayoutChild,+ matchLayoutChildTableBlock,+ videoBlockAsLayoutChild,+ matchLayoutChildVideoBlock,+ LayoutFallback,+ contextBlockAsLayoutFallback,+ matchLayoutFallbackContextBlock,+ dividerBlockAsLayoutFallback,+ matchLayoutFallbackDividerBlock,+ headerBlockAsLayoutFallback,+ matchLayoutFallbackHeaderBlock,+ imageBlockAsLayoutFallback,+ matchLayoutFallbackImageBlock,+ richTextBlockAsLayoutFallback,+ matchLayoutFallbackRichTextBlock,+ sectionBlockAsLayoutFallback,+ matchLayoutFallbackSectionBlock,+ tableBlockAsLayoutFallback,+ matchLayoutFallbackTableBlock,+ videoBlockAsLayoutFallback,+ matchLayoutFallbackVideoBlock, ) where @@ -681,9 +753,9 @@ -- | Shared Block Kit contract version implemented by this package. specVersion :: Text -packageVersion = "3.1.0"+packageVersion = "3.2.0" -specVersion = "1.4.0"+specVersion = "1.5.0" -- | Severity levels supported by alert blocks. data AlertLevel@@ -873,6 +945,78 @@ instance FromJSON ResponseType where parseJSON = checkedParser +-- | Relative layout item weights: narrow is 1, standard is 2, and wide is 3.+data LayoutWidth+ = -- | Slack wire value: @narrow@.+ LayoutWidthNarrow+ | -- | Slack wire value: @standard@.+ LayoutWidthStandard+ | -- | Slack wire value: @wide@.+ LayoutWidthWide+ deriving (Eq, Ord, Show, Enum, Bounded)++instance ToJSON LayoutWidth where+ toJSON LayoutWidthNarrow = String "narrow"+ toJSON LayoutWidthStandard = String "standard"+ toJSON LayoutWidthWide = String "wide"++instance FromSlackJSON LayoutWidth where+ fromSlackJSON value = case value of+ String "narrow" -> Right LayoutWidthNarrow+ String "standard" -> Right LayoutWidthStandard+ String "wide" -> Right LayoutWidthWide+ _ -> failure TypeMismatch [] "enum" "unknown enumeration value"++instance FromJSON LayoutWidth where parseJSON = checkedParser++-- | Direction-aware alignment within a layout item. Omit for fill behavior.+data LayoutAlignment+ = -- | Slack wire value: @start@.+ LayoutAlignmentStart+ | -- | Slack wire value: @center@.+ LayoutAlignmentCenter+ | -- | Slack wire value: @end@.+ LayoutAlignmentEnd+ deriving (Eq, Ord, Show, Enum, Bounded)++instance ToJSON LayoutAlignment where+ toJSON LayoutAlignmentStart = String "start"+ toJSON LayoutAlignmentCenter = String "center"+ toJSON LayoutAlignmentEnd = String "end"++instance FromSlackJSON LayoutAlignment where+ fromSlackJSON value = case value of+ String "start" -> Right LayoutAlignmentStart+ String "center" -> Right LayoutAlignmentCenter+ String "end" -> Right LayoutAlignmentEnd+ _ -> failure TypeMismatch [] "enum" "unknown enumeration value"++instance FromJSON LayoutAlignment where parseJSON = checkedParser++-- | Layout borders: none, outer border with horizontal row dividers, or outer border only.+data LayoutGridLines+ = -- | Slack wire value: @none@.+ LayoutGridLinesNone+ | -- | Slack wire value: @rows@.+ LayoutGridLinesRows+ | -- | Slack wire value: @outer@.+ LayoutGridLinesOuter+ deriving (Eq, Ord, Show, Enum, Bounded)++instance ToJSON LayoutGridLines where+ toJSON LayoutGridLinesNone = String "none"+ toJSON LayoutGridLinesRows = String "rows"+ toJSON LayoutGridLinesOuter = String "outer"++instance FromSlackJSON LayoutGridLines where+ fromSlackJSON value = case value of+ String "none" -> Right LayoutGridLinesNone+ String "rows" -> Right LayoutGridLinesRows+ String "outer" -> Right LayoutGridLinesOuter+ _ -> failure TypeMismatch [] "enum" "unknown enumeration value"++instance FromJSON LayoutGridLines where parseJSON = checkedParser+ -- | A block that holds interactive elements such as buttons and menus. -- -- <https://docs.slack.dev/reference/block-kit/blocks/actions-block>@@ -1099,6 +1243,7 @@ -- -- <https://docs.slack.dev/reference/block-kit/blocks/container-block> -- Provide a title or a rich text title.+-- When a layout is nested, the combined layout/container depth cannot exceed two composite levels. newtype ContainerBlock = CheckedContainerBlock ContainerBlockFields deriving (Eq, Show) -- | Editable construction input. Call 'buildContainerBlock' before encoding.@@ -5840,6 +5985,170 @@ instance FromJSON WebhookMessage where parseJSON = checkedParser +-- | A visual grid of one to five rows on messages, modals, and App Home. Supply fallback content for older clients.+--+-- <https://docs.slack.dev/reference/block-kit/blocks/layout-block>+-- Children must be valid on the receiving surface.+-- Layout and container nesting is limited to two composite levels.+-- Existing message and view limits count top-level blocks; no unconfirmed nested-block counting formula is imposed.+newtype LayoutBlock = CheckedLayoutBlock LayoutBlockFields deriving (Eq, Show)++-- | Editable construction input. Call 'buildLayoutBlock' before encoding.+data LayoutBlockFields = LayoutBlockFields+ { -- | Describes the layout. A visible label is bold text, not a semantic heading. Required. Shared limit: @layout.label@.+ layoutBlockLabel :: Text,+ -- | Adds one to five rows in source and reading order. Required. Shared limit: @layout.rows@.+ layoutBlockRows :: [LayoutRow],+ -- | Shows the label above the grid. Slack defaults to false; hidden labels are not exposed to screen readers. Optional; Nothing omits the field. Factory default: @Nothing@.+ layoutBlockIsLabelVisible :: Maybe (Bool),+ -- | Sets outer borders and horizontal row dividers. Slack defaults to none; there are no vertical dividers. Optional; Nothing omits the field. Factory default: @Nothing@.+ layoutBlockGridLines :: Maybe (LayoutGridLines),+ -- | Adds stable blocks rendered in place of the layout on older clients. Omission is allowed; Slack normalizes it to an empty array and does not synthesize content. Optional; Nothing omits the field. Factory default: @Nothing@.+ layoutBlockFallback :: Maybe ([LayoutFallback]),+ -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.+ layoutBlockBlockId :: Maybe (Text),+ -- | Unmodeled extension fields. Reserved and modeled keys are rejected.+ layoutBlockExtensions :: Map Text Value+ }+ deriving (Eq, Show)++-- | Start editable fields for LayoutBlock; required arguments follow the signature.+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.+-- Finish with 'buildLayoutBlock' to validate before JSON encoding.+layoutBlock :: Text -> [LayoutRow] -> LayoutBlockFields+layoutBlock arg0 arg1 = LayoutBlockFields (arg0) (arg1) (Nothing) (Nothing) (Nothing) (Nothing) (Map.empty)++-- | Inspect immutable construction fields. Rebuild edits with 'buildLayoutBlock' to revalidate.+inspectLayoutBlock :: LayoutBlock -> LayoutBlockFields+inspectLayoutBlock (CheckedLayoutBlock fields) = fields++-- | Validate fields, extensions and receiving context. Return structured errors or a checked LayoutBlock.+buildLayoutBlock :: LayoutBlockFields -> Result LayoutBlock+buildLayoutBlock fields = do+ checkExtensions ["label", "rows", "is_label_visible", "grid_lines", "fallback", "block_id"] (layoutBlockExtensions fields)+ validateFields "LayoutBlock" (wireLayoutBlock fields)+ pure (CheckedLayoutBlock fields)++wireLayoutBlock :: LayoutBlockFields -> Value+wireLayoutBlock fields =+ object $+ ["type" .= ("layout" :: Text)] <> ["label" .= (layoutBlockLabel fields)] <> ["rows" .= (layoutBlockRows fields)] <> optional "is_label_visible" (layoutBlockIsLabelVisible fields) <> optional "grid_lines" (layoutBlockGridLines fields) <> optional "fallback" (layoutBlockFallback fields) <> optional "block_id" (layoutBlockBlockId fields) <> extensionPairs (layoutBlockExtensions fields)++instance ToJSON LayoutBlock where toJSON (CheckedLayoutBlock fields) = wireLayoutBlock fields++instance FromSlackJSON LayoutBlock where+ fromSlackJSON value = do+ fields <- wireObject "layout" value+ parsedLabel <- required "label" fields+ parsedRows <- required "rows" fields+ parsedIsLabelVisible <- optionalField "is_label_visible" fields+ parsedGridLines <- optionalField "grid_lines" fields+ parsedFallback <- optionalField "fallback" fields+ parsedBlockId <- optionalField "block_id" fields+ buildLayoutBlock (LayoutBlockFields parsedLabel parsedRows parsedIsLabelVisible parsedGridLines parsedFallback parsedBlockId (remaining ["label", "rows", "is_label_visible", "grid_lines", "fallback", "block_id"] fields))++instance FromJSON LayoutBlock where parseJSON = checkedParser++-- | One to three layout items in reading order. A row has no alignment or discriminator.+--+-- <https://docs.slack.dev/reference/block-kit/blocks/layout-block>+-- At most one carousel, data table, or data visualization may occupy a row.+-- A data visualization must have more than half of the total row weight.+newtype LayoutRow = CheckedLayoutRow LayoutRowFields deriving (Eq, Show)++-- | Editable construction input. Call 'buildLayoutRow' before encoding.+data LayoutRowFields = LayoutRowFields+ { -- | Adds one to three items in source order. Required. Shared limit: @layout.items@.+ layoutRowItems :: [LayoutItem],+ -- | Unmodeled extension fields. Reserved and modeled keys are rejected.+ layoutRowExtensions :: Map Text Value+ }+ deriving (Eq, Show)++-- | Start editable fields for LayoutRow; required arguments follow the signature.+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.+-- Finish with 'buildLayoutRow' to validate before JSON encoding.+layoutRow :: [LayoutItem] -> LayoutRowFields+layoutRow arg0 = LayoutRowFields (arg0) (Map.empty)++-- | Inspect immutable construction fields. Rebuild edits with 'buildLayoutRow' to revalidate.+inspectLayoutRow :: LayoutRow -> LayoutRowFields+inspectLayoutRow (CheckedLayoutRow fields) = fields++-- | Validate fields, extensions and receiving context. Return structured errors or a checked LayoutRow.+buildLayoutRow :: LayoutRowFields -> Result LayoutRow+buildLayoutRow fields = do+ checkExtensions ["items"] (layoutRowExtensions fields)+ validateFields "LayoutRow" (wireLayoutRow fields)+ pure (CheckedLayoutRow fields)++wireLayoutRow :: LayoutRowFields -> Value+wireLayoutRow fields =+ object $+ [] <> ["items" .= (layoutRowItems fields)] <> extensionPairs (layoutRowExtensions fields)++instance ToJSON LayoutRow where toJSON (CheckedLayoutRow fields) = wireLayoutRow fields++instance FromSlackJSON LayoutRow where+ fromSlackJSON value = do+ fields <- wireObject "" value+ parsedItems <- required "items" fields+ buildLayoutRow (LayoutRowFields parsedItems (remaining ["items"] fields))++instance FromJSON LayoutRow where parseJSON = checkedParser++-- | One supported child block with a proportional width and optional direction-aware alignment.+--+-- <https://docs.slack.dev/reference/block-kit/blocks/layout-block>+newtype LayoutItem = CheckedLayoutItem LayoutItemFields deriving (Eq, Show)++-- | Editable construction input. Call 'buildLayoutItem' before encoding.+data LayoutItemFields = LayoutItemFields+ { -- | Sets the child block. Direct nested layouts and markdown blocks are not supported. Required.+ layoutItemBlock :: LayoutChild,+ -- | Sets the relative row weight: narrow 1, standard 2 (default), wide 3. Full is not supported. Optional; Nothing omits the field. Factory default: @Nothing@.+ layoutItemWidth :: Maybe (LayoutWidth),+ -- | Aligns content within the cell. Omission fills the cell; start and end follow text direction. Optional; Nothing omits the field. Factory default: @Nothing@.+ layoutItemHorizontalAlignment :: Maybe (LayoutAlignment),+ -- | Unmodeled extension fields. Reserved and modeled keys are rejected.+ layoutItemExtensions :: Map Text Value+ }+ deriving (Eq, Show)++-- | Start editable fields for LayoutItem; required arguments follow the signature.+-- Optional fields start absent except the explicit shared defaults; no block ID is invented.+-- Finish with 'buildLayoutItem' to validate before JSON encoding.+layoutItem :: LayoutChild -> LayoutItemFields+layoutItem arg0 = LayoutItemFields (arg0) (Nothing) (Nothing) (Map.empty)++-- | Inspect immutable construction fields. Rebuild edits with 'buildLayoutItem' to revalidate.+inspectLayoutItem :: LayoutItem -> LayoutItemFields+inspectLayoutItem (CheckedLayoutItem fields) = fields++-- | Validate fields, extensions and receiving context. Return structured errors or a checked LayoutItem.+buildLayoutItem :: LayoutItemFields -> Result LayoutItem+buildLayoutItem fields = do+ checkExtensions ["block", "width", "horizontal_alignment"] (layoutItemExtensions fields)+ validateFields "LayoutItem" (wireLayoutItem fields)+ pure (CheckedLayoutItem fields)++wireLayoutItem :: LayoutItemFields -> Value+wireLayoutItem fields =+ object $+ [] <> ["block" .= (layoutItemBlock fields)] <> optional "width" (layoutItemWidth fields) <> optional "horizontal_alignment" (layoutItemHorizontalAlignment fields) <> extensionPairs (layoutItemExtensions fields)++instance ToJSON LayoutItem where toJSON (CheckedLayoutItem fields) = wireLayoutItem fields++instance FromSlackJSON LayoutItem where+ fromSlackJSON value = do+ fields <- wireObject "" value+ parsedBlock <- required "block" fields+ parsedWidth <- optionalField "width" fields+ parsedHorizontalAlignment <- optionalField "horizontal_alignment" fields+ buildLayoutItem (LayoutItemFields parsedBlock parsedWidth parsedHorizontalAlignment (remaining ["block", "width", "horizontal_alignment"] fields))++instance FromJSON LayoutItem where parseJSON = checkedParser+ -- | A task in a plan. Unlike a standalone task card, it may be pending. newtype PlanTask = CheckedPlanTask TaskCardBlock deriving (Eq, Show) @@ -5861,7 +6170,7 @@ instance FromJSON PlanTask where parseJSON = checkedParser -- | Closed role: only checked members may enter this position.-data Block = BlockActionsBlock ActionsBlock | BlockAlertBlock AlertBlock | BlockCardBlock CardBlock | BlockCarouselBlock CarouselBlock | BlockContainerBlock ContainerBlock | BlockContextActionsBlock ContextActionsBlock | BlockContextBlock ContextBlock | BlockDataTableBlock DataTableBlock | BlockDataVisualizationBlock DataVisualizationBlock | BlockDividerBlock DividerBlock | BlockFileBlock FileBlock | BlockHeaderBlock HeaderBlock | BlockImageBlock ImageBlock | BlockInputBlock InputBlock | BlockMarkdownBlock MarkdownBlock | BlockPlanBlock PlanBlock | BlockRichTextBlock RichTextBlock | BlockSectionBlock SectionBlock | BlockTableBlock TableBlock | BlockTaskCardBlock TaskCardBlock | BlockVideoBlock VideoBlock deriving (Eq, Show)+data Block = BlockActionsBlock ActionsBlock | BlockAlertBlock AlertBlock | BlockCardBlock CardBlock | BlockCarouselBlock CarouselBlock | BlockContainerBlock ContainerBlock | BlockContextActionsBlock ContextActionsBlock | BlockContextBlock ContextBlock | BlockDataTableBlock DataTableBlock | BlockDataVisualizationBlock DataVisualizationBlock | BlockDividerBlock DividerBlock | BlockFileBlock FileBlock | BlockHeaderBlock HeaderBlock | BlockImageBlock ImageBlock | BlockInputBlock InputBlock | BlockMarkdownBlock MarkdownBlock | BlockPlanBlock PlanBlock | BlockRichTextBlock RichTextBlock | BlockSectionBlock SectionBlock | BlockTableBlock TableBlock | BlockTaskCardBlock TaskCardBlock | BlockVideoBlock VideoBlock | BlockLayoutBlock LayoutBlock deriving (Eq, Show) instance ToJSON Block where toJSON (BlockActionsBlock value) = toJSON value@@ -5885,6 +6194,7 @@ toJSON (BlockTableBlock value) = toJSON value toJSON (BlockTaskCardBlock value) = toJSON value toJSON (BlockVideoBlock value) = toJSON value+ toJSON (BlockLayoutBlock value) = toJSON value instance FromSlackJSON Block where fromSlackJSON value = do@@ -5911,6 +6221,7 @@ "table" -> BlockTableBlock <$> fromSlackJSON value "task_card" -> fromSlackJSON value >>= taskCardBlockAsBlock "video" -> BlockVideoBlock <$> fromSlackJSON value+ "layout" -> BlockLayoutBlock <$> fromSlackJSON value _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type" instance FromJSON Block where parseJSON = checkedParser@@ -6106,6 +6417,15 @@ matchBlockVideoBlock (BlockVideoBlock value) = Just value matchBlockVideoBlock _ = Nothing +-- | Place a checked LayoutBlock in the Block role. The receiving parent retains its contextual validation.+layoutBlockAsBlock :: LayoutBlock -> Block+layoutBlockAsBlock = BlockLayoutBlock++-- | Return the LayoutBlock member when present, otherwise Nothing; no unchecked cast.+matchBlockLayoutBlock :: Block -> Maybe LayoutBlock+matchBlockLayoutBlock (BlockLayoutBlock value) = Just value+matchBlockLayoutBlock _ = Nothing+ -- | Closed role: only checked members may enter this position. data ContextElement = ContextElementImageElement ImageElement | ContextElementMarkdownText MarkdownText | ContextElementPlainText PlainText deriving (Eq, Show) @@ -7049,3 +7369,304 @@ matchRichTextSectionElementRichTextUserGroup :: RichTextSectionElement -> Maybe RichTextUserGroup matchRichTextSectionElementRichTextUserGroup (RichTextSectionElementRichTextUserGroup value) = Just value matchRichTextSectionElementRichTextUserGroup _ = Nothing++-- | Closed role: only checked members may enter this position.+data LayoutChild = LayoutChildActionsBlock ActionsBlock | LayoutChildAlertBlock AlertBlock | LayoutChildCardBlock CardBlock | LayoutChildCarouselBlock CarouselBlock | LayoutChildContainerBlock ContainerBlock | LayoutChildContextBlock ContextBlock | LayoutChildDataTableBlock DataTableBlock | LayoutChildDataVisualizationBlock DataVisualizationBlock | LayoutChildDividerBlock DividerBlock | LayoutChildFileBlock FileBlock | LayoutChildHeaderBlock HeaderBlock | LayoutChildImageBlock ImageBlock | LayoutChildInputBlock InputBlock | LayoutChildRichTextBlock RichTextBlock | LayoutChildSectionBlock SectionBlock | LayoutChildTableBlock TableBlock | LayoutChildVideoBlock VideoBlock deriving (Eq, Show)++instance ToJSON LayoutChild where+ toJSON (LayoutChildActionsBlock value) = toJSON value+ toJSON (LayoutChildAlertBlock value) = toJSON value+ toJSON (LayoutChildCardBlock value) = toJSON value+ toJSON (LayoutChildCarouselBlock value) = toJSON value+ toJSON (LayoutChildContainerBlock value) = toJSON value+ toJSON (LayoutChildContextBlock value) = toJSON value+ toJSON (LayoutChildDataTableBlock value) = toJSON value+ toJSON (LayoutChildDataVisualizationBlock value) = toJSON value+ toJSON (LayoutChildDividerBlock value) = toJSON value+ toJSON (LayoutChildFileBlock value) = toJSON value+ toJSON (LayoutChildHeaderBlock value) = toJSON value+ toJSON (LayoutChildImageBlock value) = toJSON value+ toJSON (LayoutChildInputBlock value) = toJSON value+ toJSON (LayoutChildRichTextBlock value) = toJSON value+ toJSON (LayoutChildSectionBlock value) = toJSON value+ toJSON (LayoutChildTableBlock value) = toJSON value+ toJSON (LayoutChildVideoBlock value) = toJSON value++instance FromSlackJSON LayoutChild where+ fromSlackJSON value = do+ tag <- wireTag value+ case tag of+ "actions" -> LayoutChildActionsBlock <$> fromSlackJSON value+ "alert" -> LayoutChildAlertBlock <$> fromSlackJSON value+ "card" -> LayoutChildCardBlock <$> fromSlackJSON value+ "carousel" -> LayoutChildCarouselBlock <$> fromSlackJSON value+ "container" -> LayoutChildContainerBlock <$> fromSlackJSON value+ "context" -> LayoutChildContextBlock <$> fromSlackJSON value+ "data_table" -> LayoutChildDataTableBlock <$> fromSlackJSON value+ "data_visualization" -> LayoutChildDataVisualizationBlock <$> fromSlackJSON value+ "divider" -> LayoutChildDividerBlock <$> fromSlackJSON value+ "file" -> LayoutChildFileBlock <$> fromSlackJSON value+ "header" -> LayoutChildHeaderBlock <$> fromSlackJSON value+ "image" -> LayoutChildImageBlock <$> fromSlackJSON value+ "input" -> LayoutChildInputBlock <$> fromSlackJSON value+ "rich_text" -> LayoutChildRichTextBlock <$> fromSlackJSON value+ "section" -> LayoutChildSectionBlock <$> fromSlackJSON value+ "table" -> LayoutChildTableBlock <$> fromSlackJSON value+ "video" -> LayoutChildVideoBlock <$> fromSlackJSON value+ _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"++instance FromJSON LayoutChild where parseJSON = checkedParser++-- | Place a checked ActionsBlock in the LayoutChild role. The receiving parent retains its contextual validation.+actionsBlockAsLayoutChild :: ActionsBlock -> LayoutChild+actionsBlockAsLayoutChild = LayoutChildActionsBlock++-- | Return the ActionsBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildActionsBlock :: LayoutChild -> Maybe ActionsBlock+matchLayoutChildActionsBlock (LayoutChildActionsBlock value) = Just value+matchLayoutChildActionsBlock _ = Nothing++-- | Place a checked AlertBlock in the LayoutChild role. The receiving parent retains its contextual validation.+alertBlockAsLayoutChild :: AlertBlock -> LayoutChild+alertBlockAsLayoutChild = LayoutChildAlertBlock++-- | Return the AlertBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildAlertBlock :: LayoutChild -> Maybe AlertBlock+matchLayoutChildAlertBlock (LayoutChildAlertBlock value) = Just value+matchLayoutChildAlertBlock _ = Nothing++-- | Place a checked CardBlock in the LayoutChild role. The receiving parent retains its contextual validation.+cardBlockAsLayoutChild :: CardBlock -> LayoutChild+cardBlockAsLayoutChild = LayoutChildCardBlock++-- | Return the CardBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildCardBlock :: LayoutChild -> Maybe CardBlock+matchLayoutChildCardBlock (LayoutChildCardBlock value) = Just value+matchLayoutChildCardBlock _ = Nothing++-- | Place a checked CarouselBlock in the LayoutChild role. The receiving parent retains its contextual validation.+carouselBlockAsLayoutChild :: CarouselBlock -> LayoutChild+carouselBlockAsLayoutChild = LayoutChildCarouselBlock++-- | Return the CarouselBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildCarouselBlock :: LayoutChild -> Maybe CarouselBlock+matchLayoutChildCarouselBlock (LayoutChildCarouselBlock value) = Just value+matchLayoutChildCarouselBlock _ = Nothing++-- | Place a checked ContainerBlock in the LayoutChild role. The receiving parent retains its contextual validation.+containerBlockAsLayoutChild :: ContainerBlock -> LayoutChild+containerBlockAsLayoutChild = LayoutChildContainerBlock++-- | Return the ContainerBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildContainerBlock :: LayoutChild -> Maybe ContainerBlock+matchLayoutChildContainerBlock (LayoutChildContainerBlock value) = Just value+matchLayoutChildContainerBlock _ = Nothing++-- | Place a checked ContextBlock in the LayoutChild role. The receiving parent retains its contextual validation.+contextBlockAsLayoutChild :: ContextBlock -> LayoutChild+contextBlockAsLayoutChild = LayoutChildContextBlock++-- | Return the ContextBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildContextBlock :: LayoutChild -> Maybe ContextBlock+matchLayoutChildContextBlock (LayoutChildContextBlock value) = Just value+matchLayoutChildContextBlock _ = Nothing++-- | Place a checked DataTableBlock in the LayoutChild role. The receiving parent retains its contextual validation.+dataTableBlockAsLayoutChild :: DataTableBlock -> LayoutChild+dataTableBlockAsLayoutChild = LayoutChildDataTableBlock++-- | Return the DataTableBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildDataTableBlock :: LayoutChild -> Maybe DataTableBlock+matchLayoutChildDataTableBlock (LayoutChildDataTableBlock value) = Just value+matchLayoutChildDataTableBlock _ = Nothing++-- | Place a checked DataVisualizationBlock in the LayoutChild role. The receiving parent retains its contextual validation.+dataVisualizationBlockAsLayoutChild :: DataVisualizationBlock -> LayoutChild+dataVisualizationBlockAsLayoutChild = LayoutChildDataVisualizationBlock++-- | Return the DataVisualizationBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildDataVisualizationBlock :: LayoutChild -> Maybe DataVisualizationBlock+matchLayoutChildDataVisualizationBlock (LayoutChildDataVisualizationBlock value) = Just value+matchLayoutChildDataVisualizationBlock _ = Nothing++-- | Place a checked DividerBlock in the LayoutChild role. The receiving parent retains its contextual validation.+dividerBlockAsLayoutChild :: DividerBlock -> LayoutChild+dividerBlockAsLayoutChild = LayoutChildDividerBlock++-- | Return the DividerBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildDividerBlock :: LayoutChild -> Maybe DividerBlock+matchLayoutChildDividerBlock (LayoutChildDividerBlock value) = Just value+matchLayoutChildDividerBlock _ = Nothing++-- | Place a checked FileBlock in the LayoutChild role. The receiving parent retains its contextual validation.+fileBlockAsLayoutChild :: FileBlock -> LayoutChild+fileBlockAsLayoutChild = LayoutChildFileBlock++-- | Return the FileBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildFileBlock :: LayoutChild -> Maybe FileBlock+matchLayoutChildFileBlock (LayoutChildFileBlock value) = Just value+matchLayoutChildFileBlock _ = Nothing++-- | Place a checked HeaderBlock in the LayoutChild role. The receiving parent retains its contextual validation.+headerBlockAsLayoutChild :: HeaderBlock -> LayoutChild+headerBlockAsLayoutChild = LayoutChildHeaderBlock++-- | Return the HeaderBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildHeaderBlock :: LayoutChild -> Maybe HeaderBlock+matchLayoutChildHeaderBlock (LayoutChildHeaderBlock value) = Just value+matchLayoutChildHeaderBlock _ = Nothing++-- | Place a checked ImageBlock in the LayoutChild role. The receiving parent retains its contextual validation.+imageBlockAsLayoutChild :: ImageBlock -> LayoutChild+imageBlockAsLayoutChild = LayoutChildImageBlock++-- | Return the ImageBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildImageBlock :: LayoutChild -> Maybe ImageBlock+matchLayoutChildImageBlock (LayoutChildImageBlock value) = Just value+matchLayoutChildImageBlock _ = Nothing++-- | Place a checked InputBlock in the LayoutChild role. The receiving parent retains its contextual validation.+inputBlockAsLayoutChild :: InputBlock -> LayoutChild+inputBlockAsLayoutChild = LayoutChildInputBlock++-- | Return the InputBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildInputBlock :: LayoutChild -> Maybe InputBlock+matchLayoutChildInputBlock (LayoutChildInputBlock value) = Just value+matchLayoutChildInputBlock _ = Nothing++-- | Place a checked RichTextBlock in the LayoutChild role. The receiving parent retains its contextual validation.+richTextBlockAsLayoutChild :: RichTextBlock -> LayoutChild+richTextBlockAsLayoutChild = LayoutChildRichTextBlock++-- | Return the RichTextBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildRichTextBlock :: LayoutChild -> Maybe RichTextBlock+matchLayoutChildRichTextBlock (LayoutChildRichTextBlock value) = Just value+matchLayoutChildRichTextBlock _ = Nothing++-- | Place a checked SectionBlock in the LayoutChild role. The receiving parent retains its contextual validation.+sectionBlockAsLayoutChild :: SectionBlock -> LayoutChild+sectionBlockAsLayoutChild = LayoutChildSectionBlock++-- | Return the SectionBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildSectionBlock :: LayoutChild -> Maybe SectionBlock+matchLayoutChildSectionBlock (LayoutChildSectionBlock value) = Just value+matchLayoutChildSectionBlock _ = Nothing++-- | Place a checked TableBlock in the LayoutChild role. The receiving parent retains its contextual validation.+tableBlockAsLayoutChild :: TableBlock -> LayoutChild+tableBlockAsLayoutChild = LayoutChildTableBlock++-- | Return the TableBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildTableBlock :: LayoutChild -> Maybe TableBlock+matchLayoutChildTableBlock (LayoutChildTableBlock value) = Just value+matchLayoutChildTableBlock _ = Nothing++-- | Place a checked VideoBlock in the LayoutChild role. The receiving parent retains its contextual validation.+videoBlockAsLayoutChild :: VideoBlock -> LayoutChild+videoBlockAsLayoutChild = LayoutChildVideoBlock++-- | Return the VideoBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutChildVideoBlock :: LayoutChild -> Maybe VideoBlock+matchLayoutChildVideoBlock (LayoutChildVideoBlock value) = Just value+matchLayoutChildVideoBlock _ = Nothing++-- | Closed role: only checked members may enter this position.+data LayoutFallback = LayoutFallbackContextBlock ContextBlock | LayoutFallbackDividerBlock DividerBlock | LayoutFallbackHeaderBlock HeaderBlock | LayoutFallbackImageBlock ImageBlock | LayoutFallbackRichTextBlock RichTextBlock | LayoutFallbackSectionBlock SectionBlock | LayoutFallbackTableBlock TableBlock | LayoutFallbackVideoBlock VideoBlock deriving (Eq, Show)++instance ToJSON LayoutFallback where+ toJSON (LayoutFallbackContextBlock value) = toJSON value+ toJSON (LayoutFallbackDividerBlock value) = toJSON value+ toJSON (LayoutFallbackHeaderBlock value) = toJSON value+ toJSON (LayoutFallbackImageBlock value) = toJSON value+ toJSON (LayoutFallbackRichTextBlock value) = toJSON value+ toJSON (LayoutFallbackSectionBlock value) = toJSON value+ toJSON (LayoutFallbackTableBlock value) = toJSON value+ toJSON (LayoutFallbackVideoBlock value) = toJSON value++instance FromSlackJSON LayoutFallback where+ fromSlackJSON value = do+ tag <- wireTag value+ case tag of+ "context" -> LayoutFallbackContextBlock <$> fromSlackJSON value+ "divider" -> LayoutFallbackDividerBlock <$> fromSlackJSON value+ "header" -> LayoutFallbackHeaderBlock <$> fromSlackJSON value+ "image" -> LayoutFallbackImageBlock <$> fromSlackJSON value+ "rich_text" -> LayoutFallbackRichTextBlock <$> fromSlackJSON value+ "section" -> LayoutFallbackSectionBlock <$> fromSlackJSON value+ "table" -> LayoutFallbackTableBlock <$> fromSlackJSON value+ "video" -> LayoutFallbackVideoBlock <$> fromSlackJSON value+ _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type"++instance FromJSON LayoutFallback where parseJSON = checkedParser++-- | Place a checked ContextBlock in the LayoutFallback role. The receiving parent retains its contextual validation.+contextBlockAsLayoutFallback :: ContextBlock -> LayoutFallback+contextBlockAsLayoutFallback = LayoutFallbackContextBlock++-- | Return the ContextBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutFallbackContextBlock :: LayoutFallback -> Maybe ContextBlock+matchLayoutFallbackContextBlock (LayoutFallbackContextBlock value) = Just value+matchLayoutFallbackContextBlock _ = Nothing++-- | Place a checked DividerBlock in the LayoutFallback role. The receiving parent retains its contextual validation.+dividerBlockAsLayoutFallback :: DividerBlock -> LayoutFallback+dividerBlockAsLayoutFallback = LayoutFallbackDividerBlock++-- | Return the DividerBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutFallbackDividerBlock :: LayoutFallback -> Maybe DividerBlock+matchLayoutFallbackDividerBlock (LayoutFallbackDividerBlock value) = Just value+matchLayoutFallbackDividerBlock _ = Nothing++-- | Place a checked HeaderBlock in the LayoutFallback role. The receiving parent retains its contextual validation.+headerBlockAsLayoutFallback :: HeaderBlock -> LayoutFallback+headerBlockAsLayoutFallback = LayoutFallbackHeaderBlock++-- | Return the HeaderBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutFallbackHeaderBlock :: LayoutFallback -> Maybe HeaderBlock+matchLayoutFallbackHeaderBlock (LayoutFallbackHeaderBlock value) = Just value+matchLayoutFallbackHeaderBlock _ = Nothing++-- | Place a checked ImageBlock in the LayoutFallback role. The receiving parent retains its contextual validation.+imageBlockAsLayoutFallback :: ImageBlock -> LayoutFallback+imageBlockAsLayoutFallback = LayoutFallbackImageBlock++-- | Return the ImageBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutFallbackImageBlock :: LayoutFallback -> Maybe ImageBlock+matchLayoutFallbackImageBlock (LayoutFallbackImageBlock value) = Just value+matchLayoutFallbackImageBlock _ = Nothing++-- | Place a checked RichTextBlock in the LayoutFallback role. The receiving parent retains its contextual validation.+richTextBlockAsLayoutFallback :: RichTextBlock -> LayoutFallback+richTextBlockAsLayoutFallback = LayoutFallbackRichTextBlock++-- | Return the RichTextBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutFallbackRichTextBlock :: LayoutFallback -> Maybe RichTextBlock+matchLayoutFallbackRichTextBlock (LayoutFallbackRichTextBlock value) = Just value+matchLayoutFallbackRichTextBlock _ = Nothing++-- | Place a checked SectionBlock in the LayoutFallback role. The receiving parent retains its contextual validation.+sectionBlockAsLayoutFallback :: SectionBlock -> LayoutFallback+sectionBlockAsLayoutFallback = LayoutFallbackSectionBlock++-- | Return the SectionBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutFallbackSectionBlock :: LayoutFallback -> Maybe SectionBlock+matchLayoutFallbackSectionBlock (LayoutFallbackSectionBlock value) = Just value+matchLayoutFallbackSectionBlock _ = Nothing++-- | Place a checked TableBlock in the LayoutFallback role. The receiving parent retains its contextual validation.+tableBlockAsLayoutFallback :: TableBlock -> LayoutFallback+tableBlockAsLayoutFallback = LayoutFallbackTableBlock++-- | Return the TableBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutFallbackTableBlock :: LayoutFallback -> Maybe TableBlock+matchLayoutFallbackTableBlock (LayoutFallbackTableBlock value) = Just value+matchLayoutFallbackTableBlock _ = Nothing++-- | Place a checked VideoBlock in the LayoutFallback role. The receiving parent retains its contextual validation.+videoBlockAsLayoutFallback :: VideoBlock -> LayoutFallback+videoBlockAsLayoutFallback = LayoutFallbackVideoBlock++-- | Return the VideoBlock member when present, otherwise Nothing; no unchecked cast.+matchLayoutFallbackVideoBlock :: LayoutFallback -> Maybe VideoBlock+matchLayoutFallbackVideoBlock (LayoutFallbackVideoBlock value) = Just value+matchLayoutFallbackVideoBlock _ = Nothing
test/Conformance.hs view
@@ -100,5 +100,5 @@ <> show (length invalidCases) <> " invalid ingress cases and " <> show nativeCount- <> " native invalid constructions passed. Eight unrepresentable inputs have compiler rejection controls."+ <> " native invalid constructions passed. Unrepresentable inputs have compiler rejection controls." )
test/Constructions.hs view
@@ -830,7 +830,14 @@ validCases :: [(String, Result Value, Value -> Result Value)] validCases =- [ ("attachments/attachment_multi_block", valid0, \value -> toJSON <$> (fromValue value :: Result Attachment)),+ [ ("blocks/layout_chart_majority", validLayoutChartMajority, \value -> toJSON <$> (fromValue value :: Result LayoutBlock)),+ ("blocks/layout_block", validLayout0, \value -> toJSON <$> (fromValue value :: Result LayoutBlock)),+ ("blocks/layout_styled", validLayout1, \value -> toJSON <$> (fromValue value :: Result LayoutBlock)),+ ("blocks/layout_empty_fallback", validLayout2, \value -> toJSON <$> (fromValue value :: Result LayoutBlock)),+ ("blocks/layout_limits", validLayout3, \value -> toJSON <$> (fromValue value :: Result LayoutBlock)),+ ("blocks/layout_container", validLayout4, \value -> toJSON <$> (fromValue value :: Result LayoutBlock)),+ ("blocks/container_layout", validLayout5, \value -> toJSON <$> (fromValue value :: Result ContainerBlock)),+ ("attachments/attachment_multi_block", valid0, \value -> toJSON <$> (fromValue value :: Result Attachment)), ("attachments/attachment_simple", valid1, \value -> toJSON <$> (fromValue value :: Result Attachment)), ("blocks/actions_block_checkboxes", valid2, \value -> toJSON <$> (fromValue value :: Result ActionsBlock)), ("blocks/alert_block", valid3, \value -> toJSON <$> (fromValue value :: Result AlertBlock)),@@ -2144,7 +2151,28 @@ invalidCases :: [(String, Maybe (Result ()), Result ())] invalidCases =- [ ("external-query-negative", Just invalid0, void (fromValue (object ["type" .= String "external_select", "min_query_length" .= Number (-1)]) :: Result ExternalSelectElement)),+ [ ("layout-home-alert", Just invalidLayoutAudit0, void (fromValue (object ["type" .= String "home", "blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "alert", "text" .= object ["type" .= String "plain_text", "text" .= String "Modal only"]]]] :: [Value])]] :: [Value])]] :: [Value])]) :: Result HomeTabView)),+ ("layout-modal-fallback-table", Just invalidLayoutAudit1, void (fromValue (object ["type" .= String "modal", "title" .= object ["type" .= String "plain_text", "text" .= String "Grid"], "blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])]] :: [Value]), "fallback" .= toJSON ([object ["type" .= String "table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Cell"]] :: [Value])] :: [Value])]] :: [Value])]] :: [Value])]) :: Result ModalView)),+ ("layout-chart-three-items", Just invalidLayoutAudit2, void (fromValue (object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]], object ["block" .= object ["type" .= String "data_visualization", "title" .= String "Chart", "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "One", "value" .= Number 1]] :: [Value])]]], object ["block" .= object ["type" .= String "divider"]]] :: [Value])]) :: Result LayoutRow)),+ ("layout-nested-table-total", Just invalidLayoutAudit3, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "container", "title" .= object ["type" .= String "plain_text", "text" .= String "Group"], "child_blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "H"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 10000 "x")]] :: [Value])] :: [Value]), "caption" .= String "Data"]]] :: [Value])]] :: [Value])], object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "H"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 10000 "x")]] :: [Value])] :: [Value]), "caption" .= String "Data"]]] :: [Value])]] :: [Value])]] :: [Value])]] :: [Value])]) :: Result MessagePayload)),+ ("layout-message-budget", Just layoutMessageBudget, void (fromValue (object ["channel" .= String "C123", "blocks" .= replicate 51 layoutBudgetWire]) :: Result MessagePayload)),+ ("layout-view-budget", Just layoutViewBudget, void (fromValue (object ["type" .= String "home", "blocks" .= replicate 101 layoutBudgetWire]) :: Result HomeTabView)),+ ("layout-label-empty", Just invalidLayout0, void (fromValue (object ["type" .= String "layout", "label" .= String "", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])]] :: [Value])]) :: Result LayoutBlock)),+ ("layout-label-too-long", Just invalidLayout1, void (fromValue (object ["type" .= String "layout", "label" .= String (T.replicate 151 "😀"), "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])]] :: [Value])]) :: Result LayoutBlock)),+ ("layout-rows-empty", Just invalidLayout2, void (fromValue (object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([] :: [Value])]) :: Result LayoutBlock)),+ ("layout-too-many-rows", Just invalidLayout3, void (fromValue (object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])], object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])], object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])], object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])], object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])], object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])]] :: [Value])]) :: Result LayoutBlock)),+ ("layout-items-empty", Just invalidLayout4, void (fromValue (object ["items" .= toJSON ([] :: [Value])]) :: Result LayoutRow)),+ ("layout-too-many-items", Just invalidLayout5, void (fromValue (object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]], object ["block" .= object ["type" .= String "divider"]], object ["block" .= object ["type" .= String "divider"]], object ["block" .= object ["type" .= String "divider"]]] :: [Value])]) :: Result LayoutRow)),+ ("layout-markdown-child", Nothing, void (fromValue (object ["block" .= object ["type" .= String "markdown", "text" .= String "No"]]) :: Result LayoutItem)),+ ("layout-direct-child", Nothing, void (fromValue (object ["block" .= object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])]] :: [Value])]]) :: Result LayoutItem)),+ ("layout-fallback-actions", Nothing, void (fromValue (object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])]] :: [Value]), "fallback" .= toJSON ([object ["type" .= String "actions", "elements" .= toJSON ([object ["type" .= String "button", "text" .= object ["type" .= String "plain_text", "text" .= String "Go"]]] :: [Value])]] :: [Value])]) :: Result LayoutBlock)),+ ("layout-wide-siblings", Just invalidLayout9, void (fromValue (object ["items" .= toJSON ([object ["block" .= object ["type" .= String "carousel", "elements" .= toJSON ([object ["type" .= String "card", "title" .= object ["type" .= String "plain_text", "text" .= String "Card"]]] :: [Value])]], object ["block" .= object ["type" .= String "data_visualization", "title" .= String "Chart", "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "One", "value" .= Number 1]] :: [Value])]]]] :: [Value])]) :: Result LayoutRow)),+ ("layout-chart-half", Just invalidLayout10, void (fromValue (object ["items" .= toJSON ([object ["block" .= object ["type" .= String "data_visualization", "title" .= String "Chart", "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "One", "value" .= Number 1]] :: [Value])]]], object ["block" .= object ["type" .= String "divider"]]] :: [Value])]) :: Result LayoutRow)),+ ("layout-depth-three", Just invalidLayout11, void (fromValue (object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "container", "title" .= object ["type" .= String "plain_text", "text" .= String "Container"], "child_blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])]] :: [Value])]] :: [Value])]]] :: [Value])]] :: [Value])]) :: Result LayoutBlock)),+ ("layout-message-alert", Just invalidLayout12, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "alert", "text" .= object ["type" .= String "plain_text", "text" .= String "Modal only"]]]] :: [Value])]] :: [Value])]] :: [Value])]) :: Result MessagePayload)),+ ("layout-modal-input-submit", Just invalidLayout13, void (fromValue (object ["type" .= String "modal", "title" .= object ["type" .= String "plain_text", "text" .= String "Input"], "blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "input", "label" .= object ["type" .= String "plain_text", "text" .= String "Name"], "element" .= object ["type" .= String "plain_text_input", "action_id" .= String "name"]]]] :: [Value])]] :: [Value])]] :: [Value])]) :: Result ModalView)),+ ("layout-three-charts", Just invalidLayout14, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "data_visualization", "title" .= String "Chart", "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "One", "value" .= Number 1]] :: [Value])]]]] :: [Value])], object ["items" .= toJSON ([object ["block" .= object ["type" .= String "data_visualization", "title" .= String "Chart", "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "One", "value" .= Number 1]] :: [Value])]]]] :: [Value])], object ["items" .= toJSON ([object ["block" .= object ["type" .= String "data_visualization", "title" .= String "Chart", "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "One", "value" .= Number 1]] :: [Value])]]]] :: [Value])]] :: [Value])]] :: [Value])]) :: Result MessagePayload)),+ ("external-query-negative", Just invalid0, void (fromValue (object ["type" .= String "external_select", "min_query_length" .= Number (-1)]) :: Result ExternalSelectElement)), ("external-multi-query-negative", Just invalid1, void (fromValue (object ["type" .= String "multi_external_select", "min_query_length" .= Number (-1)]) :: Result ExternalMultiSelectElement)), ("date-picker-invalid-format", Just invalid2, void (fromValue (object ["type" .= String "datepicker", "initial_date" .= String "February 29, 2028"]) :: Result DatePickerElement)), ("time-picker-invalid-format", Just invalid3, void (fromValue (object ["type" .= String "timepicker", "initial_time" .= String "9:00"]) :: Result TimePickerElement)),@@ -2335,3 +2363,320 @@ ("view-external-id-too-long", Just invalid188, void (fromValue (object ["type" .= String "home", "blocks" .= toJSON ([object ["type" .= String "divider"]] :: [Value]), "external_id" .= String (T.replicate 256 "x")]) :: Result HomeTabView)), ("attachment-missing-blocks", Nothing, void (fromValue (object ["fallback" .= String "Summary"]) :: Result Attachment)) ]++validLayout0 :: Result Value+validLayout0 = do+ v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child"})+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildLayoutRow (layoutRow [v1])+ v3 <- buildLayoutBlock ((layoutBlock "Layout" [v2]) {layoutBlockBlockId = Just "layout"})+ pure (toJSON v3)++validLayout1 :: Result Value+validLayout1 = do+ v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "0"})+ v1 <- buildLayoutItem ((layoutItem (dividerBlockAsLayoutChild v0)) {layoutItemWidth = Just LayoutWidthNarrow, layoutItemHorizontalAlignment = Just LayoutAlignmentStart})+ v2 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "1"})+ v3 <- buildLayoutItem ((layoutItem (dividerBlockAsLayoutChild v2)) {layoutItemWidth = Just LayoutWidthStandard, layoutItemHorizontalAlignment = Just LayoutAlignmentCenter})+ v4 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "2"})+ v5 <- buildLayoutItem ((layoutItem (dividerBlockAsLayoutChild v4)) {layoutItemWidth = Just LayoutWidthWide, layoutItemHorizontalAlignment = Just LayoutAlignmentEnd})+ v6 <- buildLayoutRow (layoutRow [v1, v3, v5])+ v7 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "fallback"})+ v8 <- buildLayoutBlock ((layoutBlock "Status" [v6]) {layoutBlockBlockId = Just "layout", layoutBlockIsLabelVisible = Just True, layoutBlockGridLines = Just LayoutGridLinesRows, layoutBlockFallback = Just [dividerBlockAsLayoutFallback v7]})+ pure (toJSON v8)++validLayout2 :: Result Value+validLayout2 = do+ v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child"})+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildLayoutRow (layoutRow [v1])+ v3 <- buildLayoutBlock ((layoutBlock "Layout" [v2]) {layoutBlockBlockId = Just "layout", layoutBlockIsLabelVisible = Just False, layoutBlockGridLines = Just LayoutGridLinesNone, layoutBlockFallback = Just []})+ pure (toJSON v3)++validLayout3 :: Result Value+validLayout3 = do+ v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_0_0"})+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_0_1"})+ v3 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v2))+ v4 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_0_2"})+ v5 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v4))+ v6 <- buildLayoutRow (layoutRow [v1, v3, v5])+ v7 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_1_0"})+ v8 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v7))+ v9 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_1_1"})+ v10 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v9))+ v11 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_1_2"})+ v12 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v11))+ v13 <- buildLayoutRow (layoutRow [v8, v10, v12])+ v14 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_2_0"})+ v15 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v14))+ v16 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_2_1"})+ v17 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v16))+ v18 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_2_2"})+ v19 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v18))+ v20 <- buildLayoutRow (layoutRow [v15, v17, v19])+ v21 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_3_0"})+ v22 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v21))+ v23 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_3_1"})+ v24 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v23))+ v25 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_3_2"})+ v26 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v25))+ v27 <- buildLayoutRow (layoutRow [v22, v24, v26])+ v28 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_4_0"})+ v29 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v28))+ v30 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_4_1"})+ v31 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v30))+ v32 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child_4_2"})+ v33 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v32))+ v34 <- buildLayoutRow (layoutRow [v29, v31, v33])+ v35 <- buildLayoutBlock ((layoutBlock (T.replicate 150 "😀") [v6, v13, v20, v27, v34]) {layoutBlockBlockId = Just "layout", layoutBlockGridLines = Just LayoutGridLinesOuter})+ pure (toJSON v35)++validLayout4 :: Result Value+validLayout4 = do+ v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child"})+ containerTitle <- buildPlainText (plainText "Container")+ v1 <- buildContainerBlock ((containerBlock [dividerBlockAsBlock v0]) {containerBlockTitle = Just containerTitle, containerBlockWidth = Just ContainerWidthStandard, containerBlockIsCollapsible = Just False, containerBlockDefaultCollapsed = Just False, containerBlockHasHeaderDivider = Just False, containerBlockBlockId = Just "container"})+ v2 <- buildLayoutItem (layoutItem (containerBlockAsLayoutChild v1))+ v3 <- buildLayoutRow (layoutRow [v2])+ v4 <- buildLayoutBlock ((layoutBlock "Layout" [v3]) {layoutBlockBlockId = Just "layout"})+ pure (toJSON v4)++validLayout5 :: Result Value+validLayout5 = do+ v0 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child"})+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildLayoutRow (layoutRow [v1])+ v3 <- buildLayoutBlock ((layoutBlock "Layout" [v2]) {layoutBlockBlockId = Just "layout"})+ containerTitle <- buildPlainText (plainText "Container")+ v4 <- buildContainerBlock ((containerBlock [layoutBlockAsBlock v3]) {containerBlockTitle = Just containerTitle, containerBlockWidth = Just ContainerWidthStandard, containerBlockIsCollapsible = Just False, containerBlockDefaultCollapsed = Just False, containerBlockHasHeaderDivider = Just False, containerBlockBlockId = Just "container"})+ pure (toJSON v4)++invalidLayout0 :: Result ()+invalidLayout0 = do+ v0 <- buildDividerBlock dividerBlock+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildLayoutRow (layoutRow [v1])+ v3 <- buildLayoutBlock (layoutBlock "" [v2])+ v3 `seq` pure ()++invalidLayout1 :: Result ()+invalidLayout1 = do+ v0 <- buildDividerBlock dividerBlock+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildLayoutRow (layoutRow [v1])+ v3 <- buildLayoutBlock (layoutBlock (T.replicate 151 "😀") [v2])+ v3 `seq` pure ()++invalidLayout2 :: Result ()+invalidLayout2 = do+ v0 <- buildLayoutBlock (layoutBlock "Layout" [])+ v0 `seq` pure ()++invalidLayout3 :: Result ()+invalidLayout3 = do+ v0 <- buildDividerBlock dividerBlock+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildLayoutRow (layoutRow [v1])+ v3 <- buildDividerBlock dividerBlock+ v4 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v3))+ v5 <- buildLayoutRow (layoutRow [v4])+ v6 <- buildDividerBlock dividerBlock+ v7 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v6))+ v8 <- buildLayoutRow (layoutRow [v7])+ v9 <- buildDividerBlock dividerBlock+ v10 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v9))+ v11 <- buildLayoutRow (layoutRow [v10])+ v12 <- buildDividerBlock dividerBlock+ v13 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v12))+ v14 <- buildLayoutRow (layoutRow [v13])+ v15 <- buildDividerBlock dividerBlock+ v16 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v15))+ v17 <- buildLayoutRow (layoutRow [v16])+ v18 <- buildLayoutBlock (layoutBlock "Layout" [v2, v5, v8, v11, v14, v17])+ v18 `seq` pure ()++invalidLayout4 :: Result ()+invalidLayout4 = do+ v0 <- buildLayoutRow (layoutRow [])+ v0 `seq` pure ()++invalidLayout5 :: Result ()+invalidLayout5 = do+ v0 <- buildDividerBlock dividerBlock+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildDividerBlock dividerBlock+ v3 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v2))+ v4 <- buildDividerBlock dividerBlock+ v5 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v4))+ v6 <- buildDividerBlock dividerBlock+ v7 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v6))+ v8 <- buildLayoutRow (layoutRow [v1, v3, v5, v7])+ v8 `seq` pure ()++invalidLayout9 :: Result ()+invalidLayout9 = do+ v0 <- buildPlainText (plainText "Card")+ v1 <- buildCardBlock (cardBlock {cardBlockTitle = Just (plainTextAsText v0)})+ v2 <- buildCarouselBlock (carouselBlock [v1])+ v3 <- buildLayoutItem (layoutItem (carouselBlockAsLayoutChild v2))+ v5 <- buildChartSegment (chartSegment "One" 1)+ v6 <- buildPieChart (pieChart [v5])+ v7 <- buildDataVisualizationBlock (dataVisualizationBlock "Chart" (pieChartAsChart v6))+ v8 <- buildLayoutItem (layoutItem (dataVisualizationBlockAsLayoutChild v7))+ v9 <- buildLayoutRow (layoutRow [v3, v8])+ v9 `seq` pure ()++invalidLayout10 :: Result ()+invalidLayout10 = do+ v1 <- buildChartSegment (chartSegment "One" 1)+ v2 <- buildPieChart (pieChart [v1])+ v3 <- buildDataVisualizationBlock (dataVisualizationBlock "Chart" (pieChartAsChart v2))+ v4 <- buildLayoutItem (layoutItem (dataVisualizationBlockAsLayoutChild v3))+ v5 <- buildDividerBlock dividerBlock+ v6 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v5))+ v7 <- buildLayoutRow (layoutRow [v4, v6])+ v7 `seq` pure ()++invalidLayout11 :: Result ()+invalidLayout11 = do+ v0 <- buildDividerBlock dividerBlock+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildLayoutRow (layoutRow [v1])+ v3 <- buildLayoutBlock (layoutBlock "Layout" [v2])+ containerTitle <- buildPlainText (plainText "Container")+ v4 <- buildContainerBlock ((containerBlock [layoutBlockAsBlock v3]) {containerBlockTitle = Just containerTitle})+ v5 <- buildLayoutItem (layoutItem (containerBlockAsLayoutChild v4))+ v6 <- buildLayoutRow (layoutRow [v5])+ v7 <- buildLayoutBlock (layoutBlock "Layout" [v6])+ v7 `seq` pure ()++invalidLayout12 :: Result ()+invalidLayout12 = do+ v0 <- buildPlainText (plainText "Modal only")+ v1 <- buildAlertBlock (alertBlock (plainTextAsText v0))+ v2 <- buildLayoutItem (layoutItem (alertBlockAsLayoutChild v1))+ v3 <- buildLayoutRow (layoutRow [v2])+ v4 <- buildLayoutBlock (layoutBlock "Layout" [v3])+ v5 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [layoutBlockAsBlock v4]})+ v5 `seq` pure ()++invalidLayout13 :: Result ()+invalidLayout13 = do+ v0 <- buildPlainText (plainText "Input")+ v1 <- buildPlainText (plainText "Name")+ v2 <- buildPlainTextInputElement (plainTextInputElement {plainTextInputElementActionId = Just "name"})+ v3 <- buildInputBlock (inputBlock v1 (plainTextInputElementAsInputElement v2))+ v4 <- buildLayoutItem (layoutItem (inputBlockAsLayoutChild v3))+ v5 <- buildLayoutRow (layoutRow [v4])+ v6 <- buildLayoutBlock (layoutBlock "Layout" [v5])+ v7 <- buildModalView (modalView v0 [layoutBlockAsBlock v6])+ v7 `seq` pure ()++invalidLayout14 :: Result ()+invalidLayout14 = do+ v1 <- buildChartSegment (chartSegment "One" 1)+ v2 <- buildPieChart (pieChart [v1])+ v3 <- buildDataVisualizationBlock (dataVisualizationBlock "Chart" (pieChartAsChart v2))+ v4 <- buildLayoutItem (layoutItem (dataVisualizationBlockAsLayoutChild v3))+ v5 <- buildLayoutRow (layoutRow [v4])+ v7 <- buildChartSegment (chartSegment "One" 1)+ v8 <- buildPieChart (pieChart [v7])+ v9 <- buildDataVisualizationBlock (dataVisualizationBlock "Chart" (pieChartAsChart v8))+ v10 <- buildLayoutItem (layoutItem (dataVisualizationBlockAsLayoutChild v9))+ v11 <- buildLayoutRow (layoutRow [v10])+ v13 <- buildChartSegment (chartSegment "One" 1)+ v14 <- buildPieChart (pieChart [v13])+ v15 <- buildDataVisualizationBlock (dataVisualizationBlock "Chart" (pieChartAsChart v14))+ v16 <- buildLayoutItem (layoutItem (dataVisualizationBlockAsLayoutChild v15))+ v17 <- buildLayoutRow (layoutRow [v16])+ v18 <- buildLayoutBlock (layoutBlock "Layout" [v5, v11, v17])+ v19 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [layoutBlockAsBlock v18]})+ v19 `seq` pure ()++layoutBudgetBlock :: Result LayoutBlock+layoutBudgetBlock = do+ d <- buildDividerBlock dividerBlock+ i <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild d))+ r <- buildLayoutRow (layoutRow [i])+ buildLayoutBlock (layoutBlock "Layout" [r])++layoutMessageBudget :: Result ()+layoutMessageBudget = do+ l <- layoutBudgetBlock+ void (buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just (replicate 51 (layoutBlockAsBlock l))}))++layoutViewBudget :: Result ()+layoutViewBudget = do+ l <- layoutBudgetBlock+ void (buildHomeTabView (homeTabView (replicate 101 (layoutBlockAsBlock l))))++layoutBudgetWire :: Value+layoutBudgetWire = object ["type" .= String "layout", "label" .= String "Layout", "rows" .= [object ["items" .= [object ["block" .= object ["type" .= String "divider"]]]]]]++validLayoutChartMajority :: Result Value+validLayoutChartMajority = do+ v0 <- buildChartSegment (chartSegment "One" 1)+ v1 <- buildPieChart (pieChart [v0])+ v2 <- buildDataVisualizationBlock ((dataVisualizationBlock "Chart" (pieChartAsChart v1)) {dataVisualizationBlockBlockId = Just "chart"})+ v3 <- buildLayoutItem ((layoutItem (dataVisualizationBlockAsLayoutChild v2)) {layoutItemWidth = Just LayoutWidthWide})+ v4 <- buildDividerBlock (dividerBlock {dividerBlockBlockId = Just "child"})+ v5 <- buildLayoutItem ((layoutItem (dividerBlockAsLayoutChild v4)) {layoutItemWidth = Just LayoutWidthStandard})+ v6 <- buildLayoutRow (layoutRow [v3, v5])+ v7 <- buildLayoutBlock ((layoutBlock "Chart summary" [v6]) {layoutBlockBlockId = Just "layout"})+ pure (toJSON v7)++invalidLayoutAudit0 :: Result ()+invalidLayoutAudit0 = do+ v0 <- buildPlainText (plainText "Modal only")+ v1 <- buildAlertBlock (alertBlock (plainTextAsText v0))+ v2 <- buildLayoutItem (layoutItem (alertBlockAsLayoutChild v1))+ v3 <- buildLayoutRow (layoutRow [v2])+ v4 <- buildLayoutBlock (layoutBlock "Layout" [v3])+ v5 <- buildHomeTabView (homeTabView [layoutBlockAsBlock v4])+ v5 `seq` pure ()++invalidLayoutAudit1 :: Result ()+invalidLayoutAudit1 = do+ v0 <- buildPlainText (plainText "Grid")+ v1 <- buildDividerBlock dividerBlock+ v2 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v1))+ v3 <- buildLayoutRow (layoutRow [v2])+ v4 <- buildRawText (rawText "Cell")+ v5 <- buildTableBlock (tableBlock [[rawTextAsTableCell v4]])+ v6 <- buildLayoutBlock ((layoutBlock "Layout" [v3]) {layoutBlockFallback = Just [tableBlockAsLayoutFallback v5]})+ v7 <- buildModalView (modalView v0 [layoutBlockAsBlock v6])+ v7 `seq` pure ()++invalidLayoutAudit2 :: Result ()+invalidLayoutAudit2 = do+ v0 <- buildDividerBlock dividerBlock+ v1 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v0))+ v2 <- buildChartSegment (chartSegment "One" 1)+ v3 <- buildPieChart (pieChart [v2])+ v4 <- buildDataVisualizationBlock (dataVisualizationBlock "Chart" (pieChartAsChart v3))+ v5 <- buildLayoutItem (layoutItem (dataVisualizationBlockAsLayoutChild v4))+ v6 <- buildDividerBlock dividerBlock+ v7 <- buildLayoutItem (layoutItem (dividerBlockAsLayoutChild v6))+ v8 <- buildLayoutRow (layoutRow [v1, v5, v7])+ v8 `seq` pure ()++invalidLayoutAudit3 :: Result ()+invalidLayoutAudit3 = do+ v0 <- buildPlainText (plainText "Group")+ v1 <- buildRawText (rawText "H")+ v2 <- buildRawText (rawText (T.replicate 10000 "x"))+ v3 <- buildDataTableBlock (dataTableBlock [[rawTextAsDataTableCell v1], [rawTextAsDataTableCell v2]] "Data")+ v4 <- buildLayoutItem (layoutItem (dataTableBlockAsLayoutChild v3))+ v5 <- buildLayoutRow (layoutRow [v4])+ v6 <- buildLayoutBlock (layoutBlock "Layout" [v5])+ v7 <- buildRawText (rawText "H")+ v8 <- buildRawText (rawText (T.replicate 10000 "x"))+ v9 <- buildDataTableBlock (dataTableBlock [[rawTextAsDataTableCell v7], [rawTextAsDataTableCell v8]] "Data")+ v10 <- buildLayoutItem (layoutItem (dataTableBlockAsLayoutChild v9))+ v11 <- buildLayoutRow (layoutRow [v10])+ v12 <- buildLayoutBlock (layoutBlock "Layout" [v11])+ v13 <- buildContainerBlock ((containerBlock [layoutBlockAsBlock v6, layoutBlockAsBlock v12]) {containerBlockTitle = Just v0})+ v14 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [containerBlockAsBlock v13]})+ v14 `seq` pure ()
test/Foundation.hs view
@@ -2,6 +2,7 @@ import Control.Monad (unless) import Data.Aeson (ToJSON (..), object, (.=))+import Data.Maybe (isNothing) import Data.Text qualified as T import Slackblocks @@ -49,5 +50,17 @@ assert "empty prompt is present" $ either (const False) (const True) $ buildButtonElement ((buttonElement label) {buttonElementAgentPrompt = Just "", buttonElementAgentPromptDisplay = Just "Summary"})- assert "spec identity" $ specVersion == "1.4.0"+ divider <- either (fail . show) pure $ buildDividerBlock dividerBlock+ item <- either (fail . show) pure $ buildLayoutItem ((layoutItem (dividerBlockAsLayoutChild divider)) {layoutItemWidth = Just LayoutWidthWide, layoutItemHorizontalAlignment = Just LayoutAlignmentEnd})+ row <- either (fail . show) pure $ buildLayoutRow (layoutRow [item])+ grid <- either (fail . show) pure $ buildLayoutBlock (layoutBlock "Grid" [row])+ _ <- either (fail . show) pure $ buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just (replicate 50 (layoutBlockAsBlock grid))})+ _ <- either (fail . show) pure $ buildHomeTabView (homeTabView (replicate 100 (layoutBlockAsBlock grid)))+ changed <- either (fail . show) pure $ buildLayoutBlock ((inspectLayoutBlock grid) {layoutBlockFallback = Just (replicate 51 (dividerBlockAsLayoutFallback divider)), layoutBlockIsLabelVisible = Just False})+ _ <- either (fail . show) pure $ buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [layoutBlockAsBlock changed]})+ assert "layout omission preserved by editing" $ isNothing (layoutBlockFallback (inspectLayoutBlock grid))+ assert "layout false preserved" $ layoutBlockIsLabelVisible (inspectLayoutBlock changed) == Just False+ assert "layout typed width" $ layoutItemWidth (inspectLayoutItem item) == Just LayoutWidthWide+ assert "layout native decoding" $ (fromValue (toJSON changed) :: Result LayoutBlock) == Right changed+ assert "spec identity" $ specVersion == "1.5.0" putStrLn "Foundation construction, wire, Unicode, editing and prompt controls passed."
test/spec/coverage.json view
@@ -1,11 +1,11 @@ {- "spec_version": "1.4.0",+ "spec_version": "1.5.0", "capabilities": { "blocks.actions": ["blocks/actions_block_checkboxes"], "blocks.alert": ["blocks/alert_block"], "blocks.card": ["blocks/card_block"], "blocks.carousel": ["blocks/carousel_block"],- "blocks.container": ["blocks/container_block"],+ "blocks.container": ["blocks/container_block", "blocks/container_layout", "blocks/layout_container"], "blocks.context": ["blocks/context_block_text_only"], "blocks.context_actions": [ "blocks/context_actions_feedback_buttons",@@ -106,6 +106,9 @@ "rich_text.user": ["rich_text/rich_text_user_basic"], "rich_text.user_group": ["rich_text/rich_text_user_group_basic"], "views.home_tab": ["views/hometab_view"],- "views.modal": ["views/modal_with_blocks", "views/modal_without_blocks"]+ "views.modal": ["views/modal_with_blocks", "views/modal_without_blocks"],+ "blocks.layout": ["blocks/layout_block", "blocks/layout_styled", "blocks/layout_empty_fallback", "blocks/layout_limits", "blocks/layout_container", "blocks/layout_chart_majority"],+ "objects.layout_row": ["blocks/layout_styled", "blocks/layout_limits", "blocks/layout_chart_majority"],+ "objects.layout_item": ["blocks/layout_styled", "blocks/layout_chart_majority"] } }
test/spec/fixtures/invalid/manifest.json view
@@ -1,5 +1,5 @@ {- "spec_version": "1.4.0",+ "spec_version": "1.5.0", "cases": [ { "id": "text-empty",@@ -1140,6 +1140,132 @@ "category": "missing-required", "constraint": "button.agent_prompt.required_with_display", "description": "A supplied prompt display, including an empty string, requires an agent prompt"+ },+ {+ "id": "layout-label-empty",+ "category": "length-exceeded",+ "constraint": "layout.label.min_length",+ "description": "Layout labels must not be empty"+ },+ {+ "id": "layout-label-too-long",+ "category": "length-exceeded",+ "constraint": "layout.label.max_length",+ "description": "Layout labels cannot exceed 150 Unicode code points"+ },+ {+ "id": "layout-rows-empty",+ "category": "length-exceeded",+ "constraint": "layout.rows.min_items",+ "description": "A layout requires at least one row"+ },+ {+ "id": "layout-too-many-rows",+ "category": "length-exceeded",+ "constraint": "layout.rows.max_items",+ "description": "A layout cannot have more than five rows"+ },+ {+ "id": "layout-items-empty",+ "category": "length-exceeded",+ "constraint": "layout.items.min_items",+ "description": "A layout row requires at least one item"+ },+ {+ "id": "layout-too-many-items",+ "category": "length-exceeded",+ "constraint": "layout.items.max_items",+ "description": "A layout row cannot have more than three items"+ },+ {+ "id": "layout-markdown-child",+ "category": "type-mismatch",+ "constraint": "layout.child.role",+ "description": "Markdown is not a layout child"+ },+ {+ "id": "layout-direct-child",+ "category": "type-mismatch",+ "constraint": "layout.child.nested_layout",+ "description": "A layout cannot directly contain a layout"+ },+ {+ "id": "layout-fallback-actions",+ "category": "type-mismatch",+ "constraint": "layout.fallback.role",+ "description": "Actions are not a stable fallback block"+ },+ {+ "id": "layout-wide-siblings",+ "category": "invalid-usage",+ "constraint": "layout.row.wide_blocks",+ "description": "A row has at most one carousel, data table or data visualization"+ },+ {+ "id": "layout-chart-half",+ "category": "invalid-usage",+ "constraint": "layout.row.chart_weight",+ "description": "A chart requires strictly more than half the row weight"+ },+ {+ "id": "layout-depth-three",+ "category": "invalid-usage",+ "constraint": "layout.depth",+ "description": "Layout/container nesting cannot exceed two composite levels"+ },+ {+ "id": "layout-message-alert",+ "category": "type-mismatch",+ "constraint": "layout.child.surface",+ "description": "A layout preserves the receiving surface restriction of its children"+ },+ {+ "id": "layout-modal-input-submit",+ "category": "missing-required",+ "constraint": "modal.input.submit",+ "description": "An input nested in a modal layout still requires a submit label"+ },+ {+ "id": "layout-three-charts",+ "category": "length-exceeded",+ "constraint": "layout.message.chart_total",+ "description": "Nested layout charts count toward the existing two-chart message limit"+ },+ {+ "id": "layout-message-budget",+ "category": "length-exceeded",+ "constraint": "layout.surface.message_blocks",+ "description": "A message rejects 51 top-level layout blocks."+ },+ {+ "id": "layout-view-budget",+ "category": "length-exceeded",+ "constraint": "layout.surface.view_blocks",+ "description": "A view rejects 101 top-level layout blocks."+ },+ {+ "id": "layout-home-alert",+ "category": "type-mismatch",+ "constraint": "layout.home.child_surface",+ "description": "A layout must not hide a modal-only alert on Home."+ },+ {+ "id": "layout-modal-fallback-table",+ "category": "type-mismatch",+ "constraint": "layout.fallback.surface",+ "description": "A layout fallback preserves the receiving modal surface restriction on tables."+ },+ {+ "id": "layout-chart-three-items",+ "category": "invalid-usage",+ "constraint": "layout.row.three_item_chart_weight",+ "description": "A middle chart with omitted standard width has no strict majority over two standard siblings."+ },+ {+ "id": "layout-nested-table-total",+ "category": "length-exceeded",+ "constraint": "layout.message.nested_table_total",+ "description": "Data tables inside layouts inside a container still count toward the existing message aggregate text limit." } ] }
+ test/spec/fixtures/valid/blocks/container_layout.json view
@@ -0,0 +1,31 @@+{+ "type": "container",+ "child_blocks": [+ {+ "type": "layout",+ "label": "Layout",+ "rows": [+ {+ "items": [+ {+ "block": {+ "type": "divider",+ "block_id": "child"+ }+ }+ ]+ }+ ],+ "block_id": "layout"+ }+ ],+ "width": "standard",+ "is_collapsible": false,+ "default_collapsed": false,+ "has_header_divider": false,+ "block_id": "container",+ "title": {+ "type": "plain_text",+ "text": "Container"+ }+}
+ test/spec/fixtures/valid/blocks/layout_block.json view
@@ -0,0 +1,17 @@+{+ "type": "layout",+ "label": "Layout",+ "rows": [+ {+ "items": [+ {+ "block": {+ "type": "divider",+ "block_id": "child"+ }+ }+ ]+ }+ ],+ "block_id": "layout"+}
+ test/spec/fixtures/valid/blocks/layout_chart_majority.json view
@@ -0,0 +1,35 @@+{+ "type": "layout",+ "label": "Chart summary",+ "block_id": "layout",+ "rows": [+ {+ "items": [+ {+ "block": {+ "type": "data_visualization",+ "title": "Chart",+ "block_id": "chart",+ "chart": {+ "type": "pie",+ "segments": [+ {+ "label": "One",+ "value": 1+ }+ ]+ }+ },+ "width": "wide"+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child"+ },+ "width": "standard"+ }+ ]+ }+ ]+}
+ test/spec/fixtures/valid/blocks/layout_container.json view
@@ -0,0 +1,31 @@+{+ "type": "layout",+ "label": "Layout",+ "rows": [+ {+ "items": [+ {+ "block": {+ "type": "container",+ "child_blocks": [+ {+ "type": "divider",+ "block_id": "child"+ }+ ],+ "width": "standard",+ "is_collapsible": false,+ "default_collapsed": false,+ "has_header_divider": false,+ "block_id": "container",+ "title": {+ "type": "plain_text",+ "text": "Container"+ }+ }+ }+ ]+ }+ ],+ "block_id": "layout"+}
+ test/spec/fixtures/valid/blocks/layout_empty_fallback.json view
@@ -0,0 +1,20 @@+{+ "type": "layout",+ "label": "Layout",+ "rows": [+ {+ "items": [+ {+ "block": {+ "type": "divider",+ "block_id": "child"+ }+ }+ ]+ }+ ],+ "block_id": "layout",+ "is_label_visible": false,+ "grid_lines": "none",+ "fallback": []+}
+ test/spec/fixtures/valid/blocks/layout_limits.json view
@@ -0,0 +1,118 @@+{+ "type": "layout",+ "label": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀",+ "rows": [+ {+ "items": [+ {+ "block": {+ "type": "divider",+ "block_id": "child_0_0"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_0_1"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_0_2"+ }+ }+ ]+ },+ {+ "items": [+ {+ "block": {+ "type": "divider",+ "block_id": "child_1_0"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_1_1"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_1_2"+ }+ }+ ]+ },+ {+ "items": [+ {+ "block": {+ "type": "divider",+ "block_id": "child_2_0"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_2_1"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_2_2"+ }+ }+ ]+ },+ {+ "items": [+ {+ "block": {+ "type": "divider",+ "block_id": "child_3_0"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_3_1"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_3_2"+ }+ }+ ]+ },+ {+ "items": [+ {+ "block": {+ "type": "divider",+ "block_id": "child_4_0"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_4_1"+ }+ },+ {+ "block": {+ "type": "divider",+ "block_id": "child_4_2"+ }+ }+ ]+ }+ ],+ "block_id": "layout",+ "grid_lines": "outer"+}
+ test/spec/fixtures/valid/blocks/layout_styled.json view
@@ -0,0 +1,43 @@+{+ "type": "layout",+ "label": "Status",+ "rows": [+ {+ "items": [+ {+ "block": {+ "type": "divider",+ "block_id": "0"+ },+ "width": "narrow",+ "horizontal_alignment": "start"+ },+ {+ "block": {+ "type": "divider",+ "block_id": "1"+ },+ "width": "standard",+ "horizontal_alignment": "center"+ },+ {+ "block": {+ "type": "divider",+ "block_id": "2"+ },+ "width": "wide",+ "horizontal_alignment": "end"+ }+ ]+ }+ ],+ "block_id": "layout",+ "is_label_visible": true,+ "grid_lines": "rows",+ "fallback": [+ {+ "type": "divider",+ "block_id": "fallback"+ }+ ]+}
test/spec/limits.json view
@@ -168,5 +168,10 @@ "title": { "max_length": 24 }, "close": { "max_length": 24 }, "submit": { "max_length": 24 }+ },+ "layout": {+ "label": { "min_length": 1, "max_length": 150 },+ "rows": { "min_items": 1, "max_items": 5 },+ "items": { "min_items": 1, "max_items": 3 } } }
test/spec/manifest.json view
@@ -1,5 +1,5 @@ {- "spec_version": "1.4.0",+ "spec_version": "1.5.0", "fixtures": [ { "id": "attachments/attachment_multi_block",@@ -678,6 +678,48 @@ "description": "Message with the maximum two data visualization blocks", "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block/", "since": "1.3.0"+ },+ {+ "id": "blocks/layout_block",+ "description": "Minimal layout preserves omitted defaults",+ "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "since": "1.5.0"+ },+ {+ "id": "blocks/layout_styled",+ "description": "Width ratios, directional alignment, visible label and explicit fallback",+ "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "since": "1.5.0"+ },+ {+ "id": "blocks/layout_empty_fallback",+ "description": "False visibility and empty fallback remain explicit",+ "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "since": "1.5.0"+ },+ {+ "id": "blocks/layout_limits",+ "description": "Unicode label and five rows of three children at all boundaries",+ "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "since": "1.5.0"+ },+ {+ "id": "blocks/layout_container",+ "description": "A layout can contain a container",+ "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "since": "1.5.0"+ },+ {+ "id": "blocks/container_layout",+ "description": "A container can contain a layout",+ "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "since": "1.5.0"+ },+ {+ "id": "blocks/layout_chart_majority",+ "description": "A chart has three fifths of the row weight",+ "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "since": "1.5.0" } ] }
test/spec/model.json view
@@ -55,6 +55,16 @@ "package": "object", "name": "RichTextSectionElement", "description": "An inline rich text element: text, a link, an emoji, or a channel, user, or user group mention."+ },+ {+ "package": "block",+ "name": "LayoutChild",+ "description": "A block that can occupy a layout item; it must also be valid on the enclosing surface."+ },+ {+ "package": "block",+ "name": "LayoutFallback",+ "description": "A stable block that older clients render in place of a layout." } ], "enums": [@@ -228,6 +238,72 @@ "description": "Visible only to the user who triggered the response." } ]+ },+ {+ "package": "block",+ "name": "LayoutWidth",+ "description": "Relative layout item weights: narrow is 1, standard is 2, and wide is 3.",+ "constants": [+ {+ "name": "NARROW",+ "wire": "narrow",+ "description": "Weight 1."+ },+ {+ "name": "STANDARD",+ "wire": "standard",+ "description": "Weight 2 (the default)."+ },+ {+ "name": "WIDE",+ "wire": "wide",+ "description": "Weight 3."+ }+ ]+ },+ {+ "package": "block",+ "name": "LayoutAlignment",+ "description": "Direction-aware alignment within a layout item. Omit for fill behavior.",+ "constants": [+ {+ "name": "START",+ "wire": "start",+ "description": "Align to the leading edge."+ },+ {+ "name": "CENTER",+ "wire": "center",+ "description": "Center the content."+ },+ {+ "name": "END",+ "wire": "end",+ "description": "Align to the trailing edge."+ }+ ]+ },+ {+ "package": "block",+ "name": "LayoutGridLines",+ "description": "Layout borders: none, outer border with horizontal row dividers, or outer border only.",+ "constants": [+ {+ "name": "NONE",+ "wire": "none",+ "description": "No borders (the default)."+ },+ {+ "name": "ROWS",+ "wire": "rows",+ "description": "Outer border and horizontal row dividers."+ },+ {+ "name": "OUTER",+ "wire": "outer",+ "description": "Outer border only."+ }+ ] } ], "types": [@@ -238,7 +314,9 @@ "wireType": "actions", "description": "A block that holds interactive elements such as buttons and menus.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/actions-block",- "implements": [],+ "implements": [+ "LayoutChild"+ ], "defaults": {}, "fields": [ {@@ -269,7 +347,9 @@ "wireType": "alert", "description": "A severity-labelled alert, available in modals.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/alert-block",- "implements": [],+ "implements": [+ "LayoutChild"+ ], "defaults": {}, "fields": [ {@@ -309,7 +389,9 @@ "wireType": "card", "description": "A compact card with an image, text, and up to three buttons.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/card-block",- "implements": [],+ "implements": [+ "LayoutChild"+ ], "defaults": {}, "fields": [ {@@ -406,7 +488,9 @@ "wireType": "carousel", "description": "A horizontally scrolling set of cards, available in messages and App Home.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/carousel-block",- "implements": [],+ "implements": [+ "LayoutChild"+ ], "defaults": {}, "fields": [ {@@ -437,7 +521,9 @@ "wireType": "container", "description": "A titled group of blocks that can optionally collapse.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/container-block",- "implements": [],+ "implements": [+ "LayoutChild"+ ], "defaults": {}, "fields": [ {@@ -525,7 +611,8 @@ } ], "rules": [- "Provide a title or a rich text title."+ "Provide a title or a rich text title.",+ "When a layout is nested, the combined layout/container depth cannot exceed two composite levels." ] }, {@@ -566,7 +653,10 @@ "wireType": "context", "description": "A block of small, secondary images and text.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/context-block",- "implements": [],+ "implements": [+ "LayoutChild",+ "LayoutFallback"+ ], "defaults": {}, "fields": [ {@@ -597,7 +687,9 @@ "wireType": "data_table", "description": "A sortable, paginated table of raw text, numbers, and rich text.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-table-block",- "implements": [],+ "implements": [+ "LayoutChild"+ ], "defaults": { "page_size": 5, "row_header_column_index": 0@@ -658,7 +750,9 @@ "wireType": "data_visualization", "description": "A titled pie, bar, area, or line chart.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-visualization-block",- "implements": [],+ "implements": [+ "LayoutChild"+ ], "defaults": {}, "fields": [ {@@ -697,7 +791,10 @@ "wireType": "divider", "description": "A horizontal rule that separates blocks.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/divider-block",- "implements": [],+ "implements": [+ "LayoutChild",+ "LayoutFallback"+ ], "defaults": {}, "fields": [ {@@ -718,7 +815,9 @@ "wireType": "file", "description": "A remote file previously added with the files.remote API.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/file-block",- "implements": [],+ "implements": [+ "LayoutChild"+ ], "defaults": { "source": "remote" },@@ -757,7 +856,10 @@ "wireType": "header", "description": "Large, bold plain text that introduces a group of blocks.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/header-block",- "implements": [],+ "implements": [+ "LayoutChild",+ "LayoutFallback"+ ], "defaults": {}, "fields": [ {@@ -789,7 +891,10 @@ "wireType": "image", "description": "A standalone image with alternative text and an optional title.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/image-block",- "implements": [],+ "implements": [+ "LayoutChild",+ "LayoutFallback"+ ], "defaults": {}, "fields": [ {@@ -847,7 +952,9 @@ "wireType": "input", "description": "A labelled input that collects a value in a modal, App Home, or message.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/input-block",- "implements": [],+ "implements": [+ "LayoutChild"+ ], "defaults": {}, "fields": [ {@@ -985,7 +1092,9 @@ "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/rich-text-block", "implements": [ "TableCell",- "DataTableCell"+ "DataTableCell",+ "LayoutChild",+ "LayoutFallback" ], "defaults": {}, "fields": [@@ -1016,7 +1125,10 @@ "wireType": "section", "description": "Text, a grid of fields, or both, with an optional accessory element.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/section-block",- "implements": [],+ "implements": [+ "LayoutChild",+ "LayoutFallback"+ ], "defaults": {}, "fields": [ {@@ -1067,7 +1179,10 @@ "wireType": "table", "description": "A simple table of raw text and rich text cells.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/table-block",- "implements": [],+ "implements": [+ "LayoutChild",+ "LayoutFallback"+ ], "defaults": {}, "fields": [ {@@ -1179,7 +1294,10 @@ "wireType": "video", "description": "An embedded video player.", "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/video-block",- "implements": [],+ "implements": [+ "LayoutChild",+ "LayoutFallback"+ ], "defaults": {}, "fields": [ {@@ -4599,6 +4717,137 @@ "Markdown block text is limited to 12,000 characters, and data table cell text to 20,000 characters, across the whole message.", "At most two data visualization blocks may be sent per message, across blocks and attachments after helper expansion (message.data_visualizations.max_items)." ]+ },+ {+ "name": "LayoutBlock",+ "package": "block",+ "goConstructor": "NewLayoutBlock",+ "wireType": "layout",+ "description": "A visual grid of one to five rows on messages, modals, and App Home. Supply fallback content for older clients.",+ "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "implements": [],+ "defaults": {},+ "fields": [+ {+ "method": "label",+ "goMethod": "Label",+ "wire": "label",+ "kind": "string",+ "description": "Describes the layout. A visible label is bold text, not a semantic heading.",+ "required": true,+ "limits": "layout.label"+ },+ {+ "method": "rows",+ "goMethod": "Rows",+ "wire": "rows",+ "kind": "list",+ "description": "Adds one to five rows in source and reading order.",+ "type": "LayoutRow",+ "required": true,+ "limits": "layout.rows"+ },+ {+ "method": "isLabelVisible",+ "goMethod": "IsLabelVisible",+ "wire": "is_label_visible",+ "kind": "boolean",+ "description": "Shows the label above the grid. Slack defaults to false; hidden labels are not exposed to screen readers."+ },+ {+ "method": "gridLines",+ "goMethod": "GridLines",+ "wire": "grid_lines",+ "kind": "enum",+ "description": "Sets outer borders and horizontal row dividers. Slack defaults to none; there are no vertical dividers.",+ "type": "LayoutGridLines"+ },+ {+ "method": "fallback",+ "goMethod": "Fallback",+ "wire": "fallback",+ "kind": "list",+ "description": "Adds stable blocks rendered in place of the layout on older clients. Omission is allowed; Slack normalizes it to an empty array and does not synthesize content.",+ "type": "LayoutFallback"+ },+ {+ "method": "blockId",+ "goMethod": "BlockID",+ "wire": "block_id",+ "kind": "string",+ "description": "Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again.",+ "limits": "block_id"+ }+ ],+ "rules": [+ "Children must be valid on the receiving surface.",+ "Layout and container nesting is limited to two composite levels.",+ "Existing message and view limits count top-level blocks; no unconfirmed nested-block counting formula is imposed."+ ]+ },+ {+ "name": "LayoutRow",+ "package": "object",+ "goConstructor": "NewLayoutRow",+ "wireType": "",+ "description": "One to three layout items in reading order. A row has no alignment or discriminator.",+ "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "implements": [],+ "defaults": {},+ "fields": [+ {+ "method": "items",+ "goMethod": "Items",+ "wire": "items",+ "kind": "list",+ "description": "Adds one to three items in source order.",+ "type": "LayoutItem",+ "required": true,+ "limits": "layout.items"+ }+ ],+ "rules": [+ "At most one carousel, data table, or data visualization may occupy a row.",+ "A data visualization must have more than half of the total row weight."+ ]+ },+ {+ "name": "LayoutItem",+ "package": "object",+ "goConstructor": "NewLayoutItem",+ "wireType": "",+ "description": "One supported child block with a proportional width and optional direction-aware alignment.",+ "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",+ "implements": [],+ "defaults": {},+ "fields": [+ {+ "method": "block",+ "goMethod": "Block",+ "wire": "block",+ "kind": "object",+ "description": "Sets the child block. Direct nested layouts and markdown blocks are not supported.",+ "type": "LayoutChild",+ "required": true+ },+ {+ "method": "width",+ "goMethod": "Width",+ "wire": "width",+ "kind": "enum",+ "description": "Sets the relative row weight: narrow 1, standard 2 (default), wide 3. Full is not supported.",+ "type": "LayoutWidth"+ },+ {+ "method": "horizontalAlignment",+ "goMethod": "HorizontalAlignment",+ "wire": "horizontal_alignment",+ "kind": "enum",+ "description": "Aligns content within the cell. Omission fills the cell; start and end follow text direction.",+ "type": "LayoutAlignment"+ }+ ],+ "rules": [] } ] }
test/spec/vocabulary.json view
@@ -69,6 +69,7 @@ "header", "image", "input",+ "layout", "rich_text", "section", "table",@@ -88,6 +89,7 @@ "header", "image", "input",+ "layout", "markdown", "plan", "rich_text",@@ -105,6 +107,7 @@ "header", "image", "input",+ "layout", "rich_text", "section", "video"