@ng-forge/dynamic-form-mcp 0.9.0-next.12 → 0.9.0-next.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ng-forge-mcp.js +608 -2
- package/package.json +6 -6
- package/src/index.d.ts +2 -2
- package/src/index.d.ts.map +1 -1
- package/src/index.js +640 -2
- package/src/registry/addons.d.ts +52 -0
- package/src/registry/addons.d.ts.map +1 -0
- package/src/registry/index.d.ts +12 -0
- package/src/registry/index.d.ts.map +1 -1
- package/src/registry/instructions.d.ts +1 -1
- package/src/registry/instructions.d.ts.map +1 -1
- package/src/tools/lookup.tool.d.ts.map +1 -1
- package/src/tools/search.tool.d.ts.map +1 -1
package/bin/ng-forge-mcp.js
CHANGED
|
@@ -1553,6 +1553,419 @@ const tagItemTemplate = { key: 'tag', type: 'input', label: 'Tag' } as const sat
|
|
|
1553
1553
|
}
|
|
1554
1554
|
];
|
|
1555
1555
|
|
|
1556
|
+
// packages/dynamic-form-mcp/src/registry/addons.ts
|
|
1557
|
+
var UNIVERSAL_BASE_PROPS = {
|
|
1558
|
+
slot: {
|
|
1559
|
+
name: "slot",
|
|
1560
|
+
type: "'prefix' | 'suffix' | string",
|
|
1561
|
+
description: "Slot to render this addon in. `prefix` and `suffix` are universal; adapters may add more.",
|
|
1562
|
+
required: true
|
|
1563
|
+
},
|
|
1564
|
+
className: {
|
|
1565
|
+
name: "className",
|
|
1566
|
+
type: "string",
|
|
1567
|
+
description: "CSS class applied to the addon host element.",
|
|
1568
|
+
required: false
|
|
1569
|
+
},
|
|
1570
|
+
hidden: {
|
|
1571
|
+
name: "hidden",
|
|
1572
|
+
type: "boolean | Signal<boolean> | Observable<boolean>",
|
|
1573
|
+
description: "Reactive visibility \u2014 `true` removes the addon from layout. JSON-safe when boolean.",
|
|
1574
|
+
required: false
|
|
1575
|
+
},
|
|
1576
|
+
disabled: {
|
|
1577
|
+
name: "disabled",
|
|
1578
|
+
type: "boolean | Signal<boolean> | Observable<boolean>",
|
|
1579
|
+
description: "Reactive disabled state \u2014 primarily relevant for interactive kinds.",
|
|
1580
|
+
required: false
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
var ADDON_KINDS = [
|
|
1584
|
+
// ─── Core-shipped universal kinds ────────────────────────────────────────
|
|
1585
|
+
{
|
|
1586
|
+
kind: "text",
|
|
1587
|
+
category: "core",
|
|
1588
|
+
package: "@ng-forge/dynamic-forms",
|
|
1589
|
+
adapter: null,
|
|
1590
|
+
description: "Renders inline text content (currency symbol, unit label, character counter, etc.).",
|
|
1591
|
+
jsonSafe: true,
|
|
1592
|
+
props: {
|
|
1593
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1594
|
+
text: {
|
|
1595
|
+
name: "text",
|
|
1596
|
+
type: "DynamicText",
|
|
1597
|
+
description: "Text content. Supports plain strings, observables, signals, and i18n keys.",
|
|
1598
|
+
required: true
|
|
1599
|
+
}
|
|
1600
|
+
},
|
|
1601
|
+
example: `{ slot: 'prefix', kind: 'text', text: '$' }`,
|
|
1602
|
+
minimalExample: `{ slot: 'prefix', kind: 'text', text: '$' }`
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
kind: "template",
|
|
1606
|
+
category: "core",
|
|
1607
|
+
package: "@ng-forge/dynamic-forms",
|
|
1608
|
+
adapter: null,
|
|
1609
|
+
description: "Renders a named `<ng-template>` projected into <df-dynamic-form>. JSON-safe \u2014 backend ships the key, FE supplies the template.",
|
|
1610
|
+
jsonSafe: true,
|
|
1611
|
+
props: {
|
|
1612
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1613
|
+
templateKey: {
|
|
1614
|
+
name: "templateKey",
|
|
1615
|
+
type: "string",
|
|
1616
|
+
description: 'Name of an `<ng-template dfTemplate="...">` projected into the host form.',
|
|
1617
|
+
required: true
|
|
1618
|
+
}
|
|
1619
|
+
},
|
|
1620
|
+
example: `{ slot: 'suffix', kind: 'template', templateKey: 'searchIcon' }`,
|
|
1621
|
+
minimalExample: `{ slot: 'suffix', kind: 'template', templateKey: 'mySlot' }`
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
kind: "component",
|
|
1625
|
+
category: "core",
|
|
1626
|
+
package: "@ng-forge/dynamic-forms",
|
|
1627
|
+
adapter: null,
|
|
1628
|
+
description: "Renders an arbitrary Angular component. The `component` loader is a function; this kind is dropped from JSON-derived configs by the validator.",
|
|
1629
|
+
jsonSafe: false,
|
|
1630
|
+
props: {
|
|
1631
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1632
|
+
component: {
|
|
1633
|
+
name: "component",
|
|
1634
|
+
type: "() => Promise<Type<unknown> | { default: Type<unknown> }>",
|
|
1635
|
+
description: "Lazy loader for the Angular component to render.",
|
|
1636
|
+
required: true
|
|
1637
|
+
},
|
|
1638
|
+
inputs: {
|
|
1639
|
+
name: "inputs",
|
|
1640
|
+
type: "Record<string, unknown>",
|
|
1641
|
+
description: "Inputs forwarded to the rendered component.",
|
|
1642
|
+
required: false
|
|
1643
|
+
}
|
|
1644
|
+
},
|
|
1645
|
+
example: `{
|
|
1646
|
+
slot: 'prefix',
|
|
1647
|
+
kind: 'component',
|
|
1648
|
+
component: () => import('./my-component').then(m => m.MyComponent),
|
|
1649
|
+
inputs: { foo: 'bar' },
|
|
1650
|
+
}`,
|
|
1651
|
+
minimalExample: `{ slot: 'prefix', kind: 'component', component: () => import('./x') }`
|
|
1652
|
+
},
|
|
1653
|
+
// ─── PrimeNG-shipped kinds ───────────────────────────────────────────────
|
|
1654
|
+
{
|
|
1655
|
+
kind: "prime-icon",
|
|
1656
|
+
category: "adapter",
|
|
1657
|
+
package: "@ng-forge/dynamic-forms-primeng",
|
|
1658
|
+
adapter: "primeng",
|
|
1659
|
+
description: 'Renders `<i class="pi pi-{icon}">` \u2014 bare PrimeIcons name.',
|
|
1660
|
+
jsonSafe: true,
|
|
1661
|
+
props: {
|
|
1662
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1663
|
+
icon: {
|
|
1664
|
+
name: "icon",
|
|
1665
|
+
type: "string",
|
|
1666
|
+
description: 'PrimeIcons name without the `pi-` prefix (e.g., "search", "times").',
|
|
1667
|
+
required: true
|
|
1668
|
+
},
|
|
1669
|
+
ariaLabel: {
|
|
1670
|
+
name: "ariaLabel",
|
|
1671
|
+
type: "DynamicText",
|
|
1672
|
+
description: "Accessible label for icons that convey meaning. Decorative icons omit this.",
|
|
1673
|
+
required: false
|
|
1674
|
+
}
|
|
1675
|
+
},
|
|
1676
|
+
example: `{ slot: 'prefix', kind: 'prime-icon', icon: 'search', ariaLabel: 'Search' }`,
|
|
1677
|
+
minimalExample: `{ slot: 'prefix', kind: 'prime-icon', icon: 'search' }`
|
|
1678
|
+
},
|
|
1679
|
+
{
|
|
1680
|
+
kind: "prime-button",
|
|
1681
|
+
category: "adapter",
|
|
1682
|
+
package: "@ng-forge/dynamic-forms-primeng",
|
|
1683
|
+
adapter: "primeng",
|
|
1684
|
+
description: "Renders `<p-button>` with optional icon, label, severity, reactive loading, and exactly one click handler (preset, actionRef, or action).",
|
|
1685
|
+
jsonSafe: true,
|
|
1686
|
+
props: {
|
|
1687
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1688
|
+
icon: { name: "icon", type: "string", description: "PrimeIcons name without the `pi-` prefix.", required: false },
|
|
1689
|
+
label: { name: "label", type: "DynamicText", description: "Visible button label.", required: false },
|
|
1690
|
+
ariaLabel: {
|
|
1691
|
+
name: "ariaLabel",
|
|
1692
|
+
type: "DynamicText",
|
|
1693
|
+
description: "Required when icon-only (no `label`). Used as `aria-label`.",
|
|
1694
|
+
required: false
|
|
1695
|
+
},
|
|
1696
|
+
severity: {
|
|
1697
|
+
name: "severity",
|
|
1698
|
+
type: "'primary' | 'secondary' | 'success' | 'info' | 'warn' | 'danger' | 'help' | 'contrast'",
|
|
1699
|
+
description: "PrimeNG button colour variant.",
|
|
1700
|
+
required: false
|
|
1701
|
+
},
|
|
1702
|
+
loading: {
|
|
1703
|
+
name: "loading",
|
|
1704
|
+
type: "boolean | Signal<boolean> | Observable<boolean>",
|
|
1705
|
+
description: "Reactive loading state \u2014 renders the native PrimeNG spinner.",
|
|
1706
|
+
required: false
|
|
1707
|
+
},
|
|
1708
|
+
preset: {
|
|
1709
|
+
name: "preset",
|
|
1710
|
+
type: "'clear' | 'reset' | 'paste' | 'copy' | 'toggle-password-visibility' | string",
|
|
1711
|
+
description: "Built-in preset action. Mutually exclusive with `actionRef` and `action`. Adapters may extend the preset union.",
|
|
1712
|
+
required: false
|
|
1713
|
+
},
|
|
1714
|
+
actionRef: {
|
|
1715
|
+
name: "actionRef",
|
|
1716
|
+
type: "string",
|
|
1717
|
+
description: "Typed string handle to a handler registered via `provideAddonActions(...)`. Mutually exclusive with `preset` and `action`. JSON-safe.",
|
|
1718
|
+
required: false
|
|
1719
|
+
},
|
|
1720
|
+
action: {
|
|
1721
|
+
name: "action",
|
|
1722
|
+
type: "(ctx: AddonActionContext) => void",
|
|
1723
|
+
description: "Inline click handler (code-only \u2014 dropped from JSON-derived configs). Mutually exclusive with `preset` and `actionRef`.",
|
|
1724
|
+
required: false
|
|
1725
|
+
}
|
|
1726
|
+
},
|
|
1727
|
+
example: `{ slot: 'suffix', kind: 'prime-button', icon: 'times', ariaLabel: 'Clear', preset: 'clear' }`,
|
|
1728
|
+
minimalExample: `{ slot: 'suffix', kind: 'prime-button', icon: 'times', ariaLabel: 'Clear', preset: 'clear' }`
|
|
1729
|
+
},
|
|
1730
|
+
// ─── Material-shipped kinds ──────────────────────────────────────────────
|
|
1731
|
+
{
|
|
1732
|
+
kind: "mat-icon",
|
|
1733
|
+
category: "adapter",
|
|
1734
|
+
package: "@ng-forge/dynamic-forms-material",
|
|
1735
|
+
adapter: "material",
|
|
1736
|
+
description: "Renders `<mat-icon>{icon}</mat-icon>` \u2014 Material Icons ligature name.",
|
|
1737
|
+
jsonSafe: true,
|
|
1738
|
+
props: {
|
|
1739
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1740
|
+
icon: {
|
|
1741
|
+
name: "icon",
|
|
1742
|
+
type: "string",
|
|
1743
|
+
description: 'Material Icons ligature (e.g., "search", "close", "visibility").',
|
|
1744
|
+
required: true
|
|
1745
|
+
},
|
|
1746
|
+
ariaLabel: {
|
|
1747
|
+
name: "ariaLabel",
|
|
1748
|
+
type: "DynamicText",
|
|
1749
|
+
description: "Accessible label for icons that convey meaning. Decorative icons omit this.",
|
|
1750
|
+
required: false
|
|
1751
|
+
}
|
|
1752
|
+
},
|
|
1753
|
+
example: `{ slot: 'prefix', kind: 'mat-icon', icon: 'search', ariaLabel: 'Search' }`,
|
|
1754
|
+
minimalExample: `{ slot: 'prefix', kind: 'mat-icon', icon: 'search' }`
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
kind: "mat-button",
|
|
1758
|
+
category: "adapter",
|
|
1759
|
+
package: "@ng-forge/dynamic-forms-material",
|
|
1760
|
+
adapter: "material",
|
|
1761
|
+
description: "Renders `<button mat-icon-button>` (icon-only) or `<button mat-button>` (labeled) with optional color, reactive loading, and exactly one click handler (preset, actionRef, or action).",
|
|
1762
|
+
jsonSafe: true,
|
|
1763
|
+
props: {
|
|
1764
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1765
|
+
icon: { name: "icon", type: "string", description: "Material Icons ligature.", required: false },
|
|
1766
|
+
label: { name: "label", type: "DynamicText", description: "Visible button label.", required: false },
|
|
1767
|
+
ariaLabel: {
|
|
1768
|
+
name: "ariaLabel",
|
|
1769
|
+
type: "DynamicText",
|
|
1770
|
+
description: "REQUIRED when icon-only (no `label`). Used as `aria-label`.",
|
|
1771
|
+
required: false
|
|
1772
|
+
},
|
|
1773
|
+
color: {
|
|
1774
|
+
name: "color",
|
|
1775
|
+
type: "'primary' | 'accent' | 'warn'",
|
|
1776
|
+
description: "Material button color variant.",
|
|
1777
|
+
required: false
|
|
1778
|
+
},
|
|
1779
|
+
loading: {
|
|
1780
|
+
name: "loading",
|
|
1781
|
+
type: "boolean | Signal<boolean> | Observable<boolean>",
|
|
1782
|
+
description: "Reactive loading state \u2014 disables the button when truthy.",
|
|
1783
|
+
required: false
|
|
1784
|
+
},
|
|
1785
|
+
preset: {
|
|
1786
|
+
name: "preset",
|
|
1787
|
+
type: "'clear' | 'reset' | 'paste' | 'copy' | 'toggle-password-visibility' | string",
|
|
1788
|
+
description: "Built-in preset action. Mutually exclusive with `actionRef` and `action`.",
|
|
1789
|
+
required: false
|
|
1790
|
+
},
|
|
1791
|
+
actionRef: {
|
|
1792
|
+
name: "actionRef",
|
|
1793
|
+
type: "string",
|
|
1794
|
+
description: "Typed handle to a handler registered via `provideAddonActions(...)`. JSON-safe.",
|
|
1795
|
+
required: false
|
|
1796
|
+
},
|
|
1797
|
+
action: {
|
|
1798
|
+
name: "action",
|
|
1799
|
+
type: "(ctx: AddonActionContext) => void",
|
|
1800
|
+
description: "Inline click handler (code-only \u2014 dropped from JSON-derived configs).",
|
|
1801
|
+
required: false
|
|
1802
|
+
}
|
|
1803
|
+
},
|
|
1804
|
+
example: `{ slot: 'suffix', kind: 'mat-button', icon: 'close', ariaLabel: 'Clear', preset: 'clear' }`,
|
|
1805
|
+
minimalExample: `{ slot: 'suffix', kind: 'mat-button', icon: 'close', ariaLabel: 'Clear', preset: 'clear' }`
|
|
1806
|
+
},
|
|
1807
|
+
// ─── Bootstrap-shipped kinds ─────────────────────────────────────────────
|
|
1808
|
+
{
|
|
1809
|
+
kind: "bs-icon",
|
|
1810
|
+
category: "adapter",
|
|
1811
|
+
package: "@ng-forge/dynamic-forms-bootstrap",
|
|
1812
|
+
adapter: "bootstrap",
|
|
1813
|
+
description: 'Renders `<i class="bi bi-{icon}">` \u2014 Bootstrap Icons name without the `bi-` prefix.',
|
|
1814
|
+
jsonSafe: true,
|
|
1815
|
+
props: {
|
|
1816
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1817
|
+
icon: {
|
|
1818
|
+
name: "icon",
|
|
1819
|
+
type: "string",
|
|
1820
|
+
description: 'Bootstrap Icons name without the `bi-` prefix (e.g., "search", "x", "eye").',
|
|
1821
|
+
required: true
|
|
1822
|
+
},
|
|
1823
|
+
ariaLabel: {
|
|
1824
|
+
name: "ariaLabel",
|
|
1825
|
+
type: "DynamicText",
|
|
1826
|
+
description: "Accessible label for icons that convey meaning. Decorative icons omit this.",
|
|
1827
|
+
required: false
|
|
1828
|
+
}
|
|
1829
|
+
},
|
|
1830
|
+
example: `{ slot: 'prefix', kind: 'bs-icon', icon: 'search', ariaLabel: 'Search' }`,
|
|
1831
|
+
minimalExample: `{ slot: 'prefix', kind: 'bs-icon', icon: 'search' }`
|
|
1832
|
+
},
|
|
1833
|
+
{
|
|
1834
|
+
kind: "bs-button",
|
|
1835
|
+
category: "adapter",
|
|
1836
|
+
package: "@ng-forge/dynamic-forms-bootstrap",
|
|
1837
|
+
adapter: "bootstrap",
|
|
1838
|
+
description: 'Renders `<button class="btn btn-outline-{severity}">` with optional icon, label, severity, and reactive loading (swaps to a spinner).',
|
|
1839
|
+
jsonSafe: true,
|
|
1840
|
+
props: {
|
|
1841
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1842
|
+
icon: { name: "icon", type: "string", description: "Bootstrap Icons name without the `bi-` prefix.", required: false },
|
|
1843
|
+
label: { name: "label", type: "DynamicText", description: "Visible button label.", required: false },
|
|
1844
|
+
ariaLabel: {
|
|
1845
|
+
name: "ariaLabel",
|
|
1846
|
+
type: "DynamicText",
|
|
1847
|
+
description: "REQUIRED when icon-only (no `label`). Used as `aria-label`.",
|
|
1848
|
+
required: false
|
|
1849
|
+
},
|
|
1850
|
+
severity: {
|
|
1851
|
+
name: "severity",
|
|
1852
|
+
type: "'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark'",
|
|
1853
|
+
description: "Bootstrap button variant (mapped to `btn-outline-{severity}`).",
|
|
1854
|
+
required: false
|
|
1855
|
+
},
|
|
1856
|
+
loading: {
|
|
1857
|
+
name: "loading",
|
|
1858
|
+
type: "boolean | Signal<boolean> | Observable<boolean>",
|
|
1859
|
+
description: "Reactive loading state \u2014 swaps the button content for a spinner.",
|
|
1860
|
+
required: false
|
|
1861
|
+
},
|
|
1862
|
+
preset: {
|
|
1863
|
+
name: "preset",
|
|
1864
|
+
type: "'clear' | 'reset' | 'paste' | 'copy' | 'toggle-password-visibility' | string",
|
|
1865
|
+
description: "Built-in preset action. Mutually exclusive with `actionRef` and `action`.",
|
|
1866
|
+
required: false
|
|
1867
|
+
},
|
|
1868
|
+
actionRef: {
|
|
1869
|
+
name: "actionRef",
|
|
1870
|
+
type: "string",
|
|
1871
|
+
description: "Typed handle to a handler registered via `provideAddonActions(...)`. JSON-safe.",
|
|
1872
|
+
required: false
|
|
1873
|
+
},
|
|
1874
|
+
action: {
|
|
1875
|
+
name: "action",
|
|
1876
|
+
type: "(ctx: AddonActionContext) => void",
|
|
1877
|
+
description: "Inline click handler (code-only \u2014 dropped from JSON-derived configs).",
|
|
1878
|
+
required: false
|
|
1879
|
+
}
|
|
1880
|
+
},
|
|
1881
|
+
example: `{ slot: 'suffix', kind: 'bs-button', icon: 'x', ariaLabel: 'Clear', preset: 'clear' }`,
|
|
1882
|
+
minimalExample: `{ slot: 'suffix', kind: 'bs-button', icon: 'x', ariaLabel: 'Clear', preset: 'clear' }`
|
|
1883
|
+
},
|
|
1884
|
+
// ─── Ionic-shipped kinds ─────────────────────────────────────────────────
|
|
1885
|
+
{
|
|
1886
|
+
kind: "ion-icon",
|
|
1887
|
+
category: "adapter",
|
|
1888
|
+
package: "@ng-forge/dynamic-forms-ionic",
|
|
1889
|
+
adapter: "ionic",
|
|
1890
|
+
description: 'Renders `<ion-icon name="{icon}">` \u2014 Ionicons name.',
|
|
1891
|
+
jsonSafe: true,
|
|
1892
|
+
props: {
|
|
1893
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1894
|
+
icon: {
|
|
1895
|
+
name: "icon",
|
|
1896
|
+
type: "string",
|
|
1897
|
+
description: 'Ionicons name (e.g., "search-outline", "close-outline", "eye-outline").',
|
|
1898
|
+
required: true
|
|
1899
|
+
},
|
|
1900
|
+
ariaLabel: {
|
|
1901
|
+
name: "ariaLabel",
|
|
1902
|
+
type: "DynamicText",
|
|
1903
|
+
description: "Accessible label for icons that convey meaning. Decorative icons omit this.",
|
|
1904
|
+
required: false
|
|
1905
|
+
}
|
|
1906
|
+
},
|
|
1907
|
+
example: `{ slot: 'prefix', kind: 'ion-icon', icon: 'search-outline', ariaLabel: 'Search' }`,
|
|
1908
|
+
minimalExample: `{ slot: 'prefix', kind: 'ion-icon', icon: 'search-outline' }`
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
kind: "ion-button",
|
|
1912
|
+
category: "adapter",
|
|
1913
|
+
package: "@ng-forge/dynamic-forms-ionic",
|
|
1914
|
+
adapter: "ionic",
|
|
1915
|
+
description: 'Renders `<ion-button>` with optional icon, label, color, fill, and reactive loading (renders an `<ion-spinner>`). `ion-button` addons render INSIDE `<ion-input>` as native `<ion-button slot="start|end">` via an attribute-selector component so Ionic\'s `::slotted(ion-button[slot=start|end])` shadow CSS sizes them natively.',
|
|
1916
|
+
jsonSafe: true,
|
|
1917
|
+
props: {
|
|
1918
|
+
...UNIVERSAL_BASE_PROPS,
|
|
1919
|
+
icon: { name: "icon", type: "string", description: "Ionicons name.", required: false },
|
|
1920
|
+
label: { name: "label", type: "DynamicText", description: "Visible button label.", required: false },
|
|
1921
|
+
ariaLabel: {
|
|
1922
|
+
name: "ariaLabel",
|
|
1923
|
+
type: "DynamicText",
|
|
1924
|
+
description: "REQUIRED when icon-only (no `label`). Used as `aria-label`.",
|
|
1925
|
+
required: false
|
|
1926
|
+
},
|
|
1927
|
+
color: {
|
|
1928
|
+
name: "color",
|
|
1929
|
+
type: "'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'danger' | 'light' | 'medium' | 'dark'",
|
|
1930
|
+
description: "Ionic button color.",
|
|
1931
|
+
required: false
|
|
1932
|
+
},
|
|
1933
|
+
fill: {
|
|
1934
|
+
name: "fill",
|
|
1935
|
+
type: "'clear' | 'outline' | 'solid' | 'default'",
|
|
1936
|
+
description: "Ionic button fill style.",
|
|
1937
|
+
required: false
|
|
1938
|
+
},
|
|
1939
|
+
loading: {
|
|
1940
|
+
name: "loading",
|
|
1941
|
+
type: "boolean | Signal<boolean> | Observable<boolean>",
|
|
1942
|
+
description: "Reactive loading state \u2014 renders an `<ion-spinner>`.",
|
|
1943
|
+
required: false
|
|
1944
|
+
},
|
|
1945
|
+
preset: {
|
|
1946
|
+
name: "preset",
|
|
1947
|
+
type: "'clear' | 'reset' | 'paste' | 'copy' | 'toggle-password-visibility' | string",
|
|
1948
|
+
description: "Built-in preset action. Mutually exclusive with `actionRef` and `action`.",
|
|
1949
|
+
required: false
|
|
1950
|
+
},
|
|
1951
|
+
actionRef: {
|
|
1952
|
+
name: "actionRef",
|
|
1953
|
+
type: "string",
|
|
1954
|
+
description: "Typed handle to a handler registered via `provideAddonActions(...)`. JSON-safe.",
|
|
1955
|
+
required: false
|
|
1956
|
+
},
|
|
1957
|
+
action: {
|
|
1958
|
+
name: "action",
|
|
1959
|
+
type: "(ctx: AddonActionContext) => void",
|
|
1960
|
+
description: "Inline click handler (code-only \u2014 dropped from JSON-derived configs).",
|
|
1961
|
+
required: false
|
|
1962
|
+
}
|
|
1963
|
+
},
|
|
1964
|
+
example: `{ slot: 'suffix', kind: 'ion-button', icon: 'close-outline', ariaLabel: 'Clear', preset: 'clear' }`,
|
|
1965
|
+
minimalExample: `{ slot: 'suffix', kind: 'ion-button', icon: 'close-outline', ariaLabel: 'Clear', preset: 'clear' }`
|
|
1966
|
+
}
|
|
1967
|
+
];
|
|
1968
|
+
|
|
1556
1969
|
// packages/dynamic-form-mcp/src/registry/index.ts
|
|
1557
1970
|
function getFieldTypes() {
|
|
1558
1971
|
return FIELD_TYPES;
|
|
@@ -1596,6 +2009,12 @@ function getDocPage(id) {
|
|
|
1596
2009
|
function getDocPagesByCategory(category) {
|
|
1597
2010
|
return DOCUMENTATION.filter((d) => d.category === category);
|
|
1598
2011
|
}
|
|
2012
|
+
function getAddonKinds() {
|
|
2013
|
+
return ADDON_KINDS;
|
|
2014
|
+
}
|
|
2015
|
+
function getAddonKind(kind) {
|
|
2016
|
+
return ADDON_KINDS.find((k) => k.kind === kind);
|
|
2017
|
+
}
|
|
1599
2018
|
|
|
1600
2019
|
// packages/dynamic-form-mcp/src/resources/field-types.resource.ts
|
|
1601
2020
|
function registerFieldTypesResource(server) {
|
|
@@ -2301,12 +2720,13 @@ Available shorthands: \`required\`, \`email\`, \`min\`, \`max\`, \`minLength\`,
|
|
|
2301
2720
|
|
|
2302
2721
|
### Nullable Values
|
|
2303
2722
|
|
|
2304
|
-
Value fields accept a \`nullable?: boolean\` flag. When \`true\`:
|
|
2723
|
+
Value fields and checked fields (\`checkbox\`, \`toggle\`) accept a \`nullable?: boolean\` flag. When \`true\`:
|
|
2305
2724
|
|
|
2306
2725
|
- \`value\` accepts \`null\` in addition to the field's normal value type.
|
|
2307
|
-
- An omitted \`value\` resolves to \`null\` (instead of the type-specific empty default like \`''\`, \`NaN\`, or \`[]\`).
|
|
2726
|
+
- An omitted \`value\` resolves to \`null\` (instead of the type-specific empty default like \`''\`, \`NaN\`, \`false\`, or \`[]\`).
|
|
2308
2727
|
- Orthogonal to \`required\`. \`nullable\` declares that the **model** accepts \`null\`; \`required\` is a **validation** constraint. Angular's \`Validators.required\` treats \`null\` as invalid, so a field that is both \`nullable\` and \`required\` will fail required-validation when the value is \`null\` \u2014 the two flags describe different layers (data shape vs. validation), not conflicting semantics.
|
|
2309
2728
|
- Map OpenAPI \`nullable: true\` (3.0) or \`type: [T, null]\` (3.1) to this flag.
|
|
2729
|
+
- Container fields (\`group\`, \`array\`, \`row\`, \`page\`) do NOT support \`nullable\` \u2014 their null semantics are ambiguous.
|
|
2310
2730
|
|
|
2311
2731
|
\`\`\`typescript
|
|
2312
2732
|
{
|
|
@@ -2316,6 +2736,14 @@ Value fields accept a \`nullable?: boolean\` flag. When \`true\`:
|
|
|
2316
2736
|
nullable: true,
|
|
2317
2737
|
value: null, // valid; default-resolves to null
|
|
2318
2738
|
}
|
|
2739
|
+
|
|
2740
|
+
{
|
|
2741
|
+
key: 'active',
|
|
2742
|
+
type: 'checkbox',
|
|
2743
|
+
label: 'Active',
|
|
2744
|
+
nullable: true,
|
|
2745
|
+
value: null, // undecided checkbox state \u2014 model permits null, UI renders unchecked
|
|
2746
|
+
}
|
|
2319
2747
|
\`\`\`
|
|
2320
2748
|
|
|
2321
2749
|
Read-side caveat: a user clearing a text input reads back as \`""\`, not \`null\` \u2014 this is a DOM/Web IDL contract, identical to classic Reactive Forms. \`nullable\` is a contract for accepted values, not a guarantee of emitted ones.
|
|
@@ -2486,6 +2914,53 @@ Available adapters:
|
|
|
2486
2914
|
- \`@ng-forge/dynamic-forms-primeng\` - PrimeNG
|
|
2487
2915
|
- \`@ng-forge/dynamic-forms-ionic\` - Ionic
|
|
2488
2916
|
|
|
2917
|
+
### Style Defaults (opt-in)
|
|
2918
|
+
|
|
2919
|
+
Each adapter ships an opinionated Sass partial with host-framework colors/typography. Library SCSS uses \`var(--df-name, structural-fallback)\` so forms render usefully without any opt-in \u2014 only opt in if you want the adapter-themed look.
|
|
2920
|
+
|
|
2921
|
+
\`\`\`scss
|
|
2922
|
+
@use '@ng-forge/dynamic-forms-material/styles/defaults' as df;
|
|
2923
|
+
|
|
2924
|
+
:root {
|
|
2925
|
+
@include df.apply-material-form-defaults;
|
|
2926
|
+
}
|
|
2927
|
+
\`\`\`
|
|
2928
|
+
|
|
2929
|
+
Substitute \`material\` for \`bootstrap\`, \`primeng\`, or \`ionic\`. Material also ships \`form-material-defaults-dark\` for dark-mode hint/error colors. Ionic ships \`apply-ionic-legacy-error-styles\` that pairs with \`withLegacyStatusClasses()\` (see below).
|
|
2930
|
+
|
|
2931
|
+
## Provider Features
|
|
2932
|
+
|
|
2933
|
+
\`provideDynamicForm()\` accepts optional \`with*\` feature configurators alongside the adapter field bundle:
|
|
2934
|
+
|
|
2935
|
+
### \`withLegacyStatusClasses()\` \u2014 opt into legacy ng-* CSS classes
|
|
2936
|
+
|
|
2937
|
+
By default, Angular Signal Forms uses its modern class strategy. If your CSS targets \`.ng-touched\`, \`.ng-invalid\`, \`.ng-dirty\`, etc. on form-bound elements, add this feature to wire up the compat-class strategy:
|
|
2938
|
+
|
|
2939
|
+
\`\`\`typescript
|
|
2940
|
+
import { provideDynamicForm, withLegacyStatusClasses } from '@ng-forge/dynamic-forms';
|
|
2941
|
+
import { withMaterialFields } from '@ng-forge/dynamic-forms-material';
|
|
2942
|
+
|
|
2943
|
+
export const appConfig = {
|
|
2944
|
+
providers: [
|
|
2945
|
+
provideDynamicForm(
|
|
2946
|
+
...withMaterialFields(),
|
|
2947
|
+
withLegacyStatusClasses(),
|
|
2948
|
+
),
|
|
2949
|
+
],
|
|
2950
|
+
};
|
|
2951
|
+
\`\`\`
|
|
2952
|
+
|
|
2953
|
+
Recommended when consuming Ionic's \`apply-ionic-legacy-error-styles\` mixin, when using Bootstrap themes that style \`is-invalid.ng-touched\`, or any custom CSS that depends on the \`ng-*\` classes.
|
|
2954
|
+
|
|
2955
|
+
### Other built-in features
|
|
2956
|
+
|
|
2957
|
+
- \`withLoggerConfig(options?)\` \u2014 configure or disable the dynamic-form logger
|
|
2958
|
+
- \`withEventFormValue()\` \u2014 attach the current form value to every dispatched event
|
|
2959
|
+
- \`withValueExclusionDefaults(options)\` \u2014 set form-wide value exclusion defaults
|
|
2960
|
+
- \`withValidationExecutionDefaults(options)\` \u2014 set form-wide validation execution defaults (e.g. defer until touched)
|
|
2961
|
+
|
|
2962
|
+
All features are exported from \`@ng-forge/dynamic-forms\`.
|
|
2963
|
+
|
|
2489
2964
|
## Common Patterns
|
|
2490
2965
|
|
|
2491
2966
|
### Always End with Submit Button
|
|
@@ -9382,6 +9857,7 @@ function getTopicList() {
|
|
|
9382
9857
|
const fieldTypes = ["input", "select", "slider", "radio", "checkbox", "textarea", "datepicker", "toggle", "text", "hidden"];
|
|
9383
9858
|
const containers = ["group", "row", "array", "simplified-array", "page"];
|
|
9384
9859
|
const wrappers = ["wrappers", ...getWrappers().map((w) => w.type)];
|
|
9860
|
+
const addons = ["addons", ...getAddonKinds().map((k) => k.kind)];
|
|
9385
9861
|
const concepts = [
|
|
9386
9862
|
"validation",
|
|
9387
9863
|
"validation-messages",
|
|
@@ -9420,6 +9896,9 @@ ${containers.map(formatTopicItem).join("\n")}
|
|
|
9420
9896
|
## Wrappers
|
|
9421
9897
|
${wrappers.map(formatTopicItem).join("\n")}
|
|
9422
9898
|
|
|
9899
|
+
## Addons
|
|
9900
|
+
${addons.map(formatTopicItem).join("\n")}
|
|
9901
|
+
|
|
9423
9902
|
## Concepts
|
|
9424
9903
|
${concepts.map(formatTopicItem).join("\n")}
|
|
9425
9904
|
|
|
@@ -9588,7 +10067,120 @@ const config = {
|
|
|
9588
10067
|
lines.push(wrappers[0]?.contract ?? "");
|
|
9589
10068
|
return lines.join("\n");
|
|
9590
10069
|
}
|
|
10070
|
+
function formatAddonInfo(addon, depth) {
|
|
10071
|
+
if (depth === "brief") {
|
|
10072
|
+
return [
|
|
10073
|
+
`# ${addon.kind} addon`,
|
|
10074
|
+
"",
|
|
10075
|
+
`${addon.description.split(".")[0]}.`,
|
|
10076
|
+
"",
|
|
10077
|
+
"```typescript",
|
|
10078
|
+
addon.minimalExample,
|
|
10079
|
+
"```",
|
|
10080
|
+
addon.jsonSafe ? "" : "**Code-only** \u2014 dropped by the lenient validator when the config came from JSON."
|
|
10081
|
+
].filter(Boolean).join("\n");
|
|
10082
|
+
}
|
|
10083
|
+
const lines = [];
|
|
10084
|
+
lines.push(`# ${addon.kind} addon`);
|
|
10085
|
+
lines.push("");
|
|
10086
|
+
lines.push(`**Category:** ${addon.category}`);
|
|
10087
|
+
lines.push(`**Package:** \`${addon.package}\``);
|
|
10088
|
+
if (addon.adapter) lines.push(`**Adapter:** ${addon.adapter}`);
|
|
10089
|
+
lines.push(`**JSON-safe:** ${addon.jsonSafe ? "Yes" : "No (code-only)"}`);
|
|
10090
|
+
lines.push(`**Description:** ${addon.description}`);
|
|
10091
|
+
if (Object.keys(addon.props).length > 0) {
|
|
10092
|
+
lines.push("");
|
|
10093
|
+
lines.push("## Config Props");
|
|
10094
|
+
for (const prop of Object.values(addon.props)) {
|
|
10095
|
+
const req = prop.required ? " **(required)**" : "";
|
|
10096
|
+
lines.push(`- \`${prop.name}\`: ${prop.type}${req} \u2014 ${prop.description}`);
|
|
10097
|
+
}
|
|
10098
|
+
}
|
|
10099
|
+
lines.push("");
|
|
10100
|
+
lines.push("## Minimal Example");
|
|
10101
|
+
lines.push("```typescript");
|
|
10102
|
+
lines.push(addon.minimalExample);
|
|
10103
|
+
lines.push("```");
|
|
10104
|
+
lines.push("");
|
|
10105
|
+
lines.push("## Full Example");
|
|
10106
|
+
lines.push("```typescript");
|
|
10107
|
+
lines.push(addon.example);
|
|
10108
|
+
lines.push("```");
|
|
10109
|
+
return lines.join("\n");
|
|
10110
|
+
}
|
|
10111
|
+
function formatAddonsOverview() {
|
|
10112
|
+
const kinds = getAddonKinds();
|
|
10113
|
+
const lines = [];
|
|
10114
|
+
lines.push("# Addons");
|
|
10115
|
+
lines.push("");
|
|
10116
|
+
lines.push(
|
|
10117
|
+
"Addons render inside a field slot \u2014 icons, buttons, inline text, custom templates, or custom components. Multiple addons in the same slot render left-to-right (mirrored in RTL). `hidden` and `disabled` are reactive (signals, observables, or `FormStateCondition`)."
|
|
10118
|
+
);
|
|
10119
|
+
lines.push("");
|
|
10120
|
+
lines.push("## Applying an addon");
|
|
10121
|
+
lines.push("");
|
|
10122
|
+
lines.push("```typescript");
|
|
10123
|
+
lines.push(`{
|
|
10124
|
+
key: 'q',
|
|
10125
|
+
type: 'input',
|
|
10126
|
+
label: 'Search',
|
|
10127
|
+
addons: [
|
|
10128
|
+
{ slot: 'prefix', kind: 'mat-icon', icon: 'search', ariaLabel: 'Search' },
|
|
10129
|
+
{ slot: 'suffix', kind: 'mat-button', icon: 'close', ariaLabel: 'Clear', preset: 'clear' },
|
|
10130
|
+
],
|
|
10131
|
+
}`);
|
|
10132
|
+
lines.push("```");
|
|
10133
|
+
lines.push("");
|
|
10134
|
+
lines.push("## Registered Kinds");
|
|
10135
|
+
lines.push("");
|
|
10136
|
+
const core = kinds.filter((k) => k.category === "core");
|
|
10137
|
+
const adapter = kinds.filter((k) => k.category === "adapter");
|
|
10138
|
+
if (core.length > 0) {
|
|
10139
|
+
lines.push("### Core (universal \u2014 ship from `@ng-forge/dynamic-forms`)");
|
|
10140
|
+
for (const k of core) {
|
|
10141
|
+
const json = k.jsonSafe ? "" : " _(code-only)_";
|
|
10142
|
+
lines.push(`- **${k.kind}**${json} \u2014 ${k.description.split(".")[0]}.`);
|
|
10143
|
+
}
|
|
10144
|
+
lines.push("");
|
|
10145
|
+
}
|
|
10146
|
+
if (adapter.length > 0) {
|
|
10147
|
+
lines.push("### Adapter (provided by UI integration packages)");
|
|
10148
|
+
for (const k of adapter) {
|
|
10149
|
+
lines.push(`- **${k.kind}** (${k.adapter}, \`${k.package}\`) \u2014 ${k.description.split(".")[0]}.`);
|
|
10150
|
+
}
|
|
10151
|
+
lines.push("");
|
|
10152
|
+
}
|
|
10153
|
+
lines.push('Use `ngforge_lookup topic="<kind>"` (e.g., `mat-button`, `pi-icon`) for full details on any individual addon kind.');
|
|
10154
|
+
lines.push("");
|
|
10155
|
+
lines.push("## Click variants (mutually exclusive on button-like addons)");
|
|
10156
|
+
lines.push("");
|
|
10157
|
+
lines.push("- `preset`: built-in semantics \u2014 `clear` / `reset` / `paste` / `copy` / `toggle-password-visibility`.");
|
|
10158
|
+
lines.push("- `actionRef`: named handler resolved via `provideAddonActions({ ... })`.");
|
|
10159
|
+
lines.push("- `action`: inline function (code-only, dropped by JSON validator).");
|
|
10160
|
+
lines.push("");
|
|
10161
|
+
lines.push("Setting more than one logs a warning and applies the first (precedence: `preset` > `actionRef` > `action`).");
|
|
10162
|
+
return lines.join("\n");
|
|
10163
|
+
}
|
|
9591
10164
|
async function resolveTopicContent(resolvedTopic, depth, uiIntegration) {
|
|
10165
|
+
if (resolvedTopic === "addons" || resolvedTopic === "addon" || resolvedTopic === "addon-kinds") {
|
|
10166
|
+
if (depth === "brief") {
|
|
10167
|
+
const kinds = getAddonKinds();
|
|
10168
|
+
return [
|
|
10169
|
+
"**Addons** render inside a field slot (prefix / suffix / adapter-specific): icons, buttons, inline text, templates, components.",
|
|
10170
|
+
"",
|
|
10171
|
+
"Registered kinds:",
|
|
10172
|
+
...kinds.map((k) => `- \`${k.kind}\` (${k.category}${k.adapter ? `, ${k.adapter}` : ""})${k.jsonSafe ? "" : " \u2014 code-only"}`),
|
|
10173
|
+
"",
|
|
10174
|
+
'Use `addons: [{ slot: "suffix", kind: "<kind>", ... }]` on an addon-supporting field. See `ngforge_lookup topic="addons"` for full docs.'
|
|
10175
|
+
].join("\n");
|
|
10176
|
+
}
|
|
10177
|
+
return formatAddonsOverview();
|
|
10178
|
+
}
|
|
10179
|
+
const addonInfo = getAddonKind(resolvedTopic);
|
|
10180
|
+
const collidesWithFieldOrWrapper = resolvedTopic === "text" || !!getWrapper(resolvedTopic);
|
|
10181
|
+
if (addonInfo && !collidesWithFieldOrWrapper) {
|
|
10182
|
+
return formatAddonInfo(addonInfo, depth);
|
|
10183
|
+
}
|
|
9592
10184
|
if (resolvedTopic === "wrappers" || resolvedTopic === "wrapper") {
|
|
9593
10185
|
if (depth === "brief") {
|
|
9594
10186
|
const wrappers = getWrappers();
|
|
@@ -9739,6 +10331,8 @@ Containers: group, row, array, simplified-array, page
|
|
|
9739
10331
|
|
|
9740
10332
|
Wrappers: wrappers (overview), css (shipping), section, arraySection (demo-only)
|
|
9741
10333
|
|
|
10334
|
+
Addons: addons (overview), text, template, component (core), mat-icon, mat-button, bs-icon, bs-button, pi-icon, pi-button, ion-icon, ion-button
|
|
10335
|
+
|
|
9742
10336
|
Concepts: validation, conditional, derivation, property-derivation, options-format, expression-variables, async-validators
|
|
9743
10337
|
|
|
9744
10338
|
Patterns: field-placement, logic-matrix, context-api, containers, multi-page-gotchas
|
|
@@ -13196,6 +13790,18 @@ function buildIndex() {
|
|
|
13196
13790
|
toolCall: `ngforge_lookup topic="${wrapper.type}"`
|
|
13197
13791
|
});
|
|
13198
13792
|
}
|
|
13793
|
+
for (const addon of getAddonKinds()) {
|
|
13794
|
+
const subcategory = addon.adapter ? `Addon (${addon.adapter})` : `Addon (${addon.category})`;
|
|
13795
|
+
entries.push({
|
|
13796
|
+
id: addon.kind,
|
|
13797
|
+
category: "addon",
|
|
13798
|
+
subcategory,
|
|
13799
|
+
description: addon.description,
|
|
13800
|
+
aliases: [],
|
|
13801
|
+
content: `${addon.kind} addon ${addon.description} ${addon.package}`.toLowerCase(),
|
|
13802
|
+
toolCall: `ngforge_lookup topic="${addon.kind}"`
|
|
13803
|
+
});
|
|
13804
|
+
}
|
|
13199
13805
|
searchIndex = entries;
|
|
13200
13806
|
return entries;
|
|
13201
13807
|
}
|