diff --git a/.github/workflows/check-build.yml b/.github/workflows/check-build.yml index 3609756572..356c8bbf6a 100644 --- a/.github/workflows/check-build.yml +++ b/.github/workflows/check-build.yml @@ -17,7 +17,7 @@ jobs: node-version: ['>=18.18.x'] steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.12.1 + uses: styfle/cancel-workflow-action@0.13.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} @@ -28,7 +28,7 @@ jobs: corepack enable - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6.3.0 + uses: actions/setup-node@v6.4.0 with: node-version: ${{ matrix.node-version }} cache: yarn diff --git a/.github/workflows/check-lint.yml b/.github/workflows/check-lint.yml index 6e9e6d7c95..05034f9b48 100644 --- a/.github/workflows/check-lint.yml +++ b/.github/workflows/check-lint.yml @@ -15,7 +15,7 @@ jobs: node-version: ['>=18.18.x'] steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.12.1 + uses: styfle/cancel-workflow-action@0.13.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} @@ -26,7 +26,7 @@ jobs: corepack enable - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6.3.0 + uses: actions/setup-node@v6.4.0 with: node-version: ${{ matrix.node-version }} cache: yarn @@ -45,7 +45,7 @@ jobs: node-version: ['>=18.18.x'] steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.12.1 + uses: styfle/cancel-workflow-action@0.13.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} @@ -56,7 +56,7 @@ jobs: corepack enable - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6.3.0 + uses: actions/setup-node@v6.4.0 with: node-version: ${{ matrix.node-version }} cache: yarn @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.12.1 + uses: styfle/cancel-workflow-action@0.13.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/check-pr-title.yml b/.github/workflows/check-pr-title.yml index 34f04795df..b049ab0c40 100644 --- a/.github/workflows/check-pr-title.yml +++ b/.github/workflows/check-pr-title.yml @@ -17,7 +17,7 @@ jobs: corepack enable - name: Set up Node.js environment - uses: actions/setup-node@v6.3.0 + uses: actions/setup-node@v6.4.0 with: node-version: '>=18.18.x' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6228d7cb84..a305f3e084 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.12.1 + uses: styfle/cancel-workflow-action@0.13.1 with: access_token: ${{ github.token }} diff --git a/.github/workflows/deploy-storybook-docs.yml b/.github/workflows/deploy-storybook-docs.yml index 44549ffbec..3bc4580647 100644 --- a/.github/workflows/deploy-storybook-docs.yml +++ b/.github/workflows/deploy-storybook-docs.yml @@ -33,7 +33,7 @@ jobs: corepack enable - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6.3.0 + uses: actions/setup-node@v6.4.0 with: node-version: ${{ matrix.node-version }} cache: yarn diff --git a/.github/workflows/release-dapps.yml b/.github/workflows/release-dapps.yml index b1078218b2..ca1da737c7 100644 --- a/.github/workflows/release-dapps.yml +++ b/.github/workflows/release-dapps.yml @@ -42,7 +42,7 @@ jobs: corepack enable - name: Setup Node.js environment - uses: actions/setup-node@v6.3.0 + uses: actions/setup-node@v6.4.0 with: node-version: '>=18.18.x' cache: yarn diff --git a/.github/workflows/release-libs.yml b/.github/workflows/release-libs.yml index 560447304b..22c02424f0 100644 --- a/.github/workflows/release-libs.yml +++ b/.github/workflows/release-libs.yml @@ -66,7 +66,7 @@ jobs: corepack enable - name: Setup Node.js environment - uses: actions/setup-node@v6.3.0 + uses: actions/setup-node@v6.4.0 with: node-version: '>=18.18.x' cache: yarn diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e97e5607b..7628a74e68 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.12.1 + uses: styfle/cancel-workflow-action@0.13.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} @@ -30,7 +30,7 @@ jobs: corepack enable - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6.3.0 + uses: actions/setup-node@v6.4.0 with: node-version: ${{ matrix.node-version }} cache: yarn diff --git a/apps/tangle-cloud/src/components/blueprintApps/BlueprintHostCard.tsx b/apps/tangle-cloud/src/components/blueprintApps/BlueprintHostCard.tsx new file mode 100644 index 0000000000..6922d76c9e --- /dev/null +++ b/apps/tangle-cloud/src/components/blueprintApps/BlueprintHostCard.tsx @@ -0,0 +1,156 @@ +import type { Blueprint } from '@tangle-network/tangle-shared-ui/types/blueprint'; +import { + Card, + CardVariant, + Typography, + Button, +} from '@tangle-network/ui-components'; +import { ExternalLinkLine } from '@tangle-network/icons'; + +const SURFACE_LABELS: Record< + NonNullable['surfaces'][number], + string +> = { + 'generic-overview': 'Overview', + 'service-explorer': 'Service explorer', + 'service-console': 'Service console', + 'actions-panel': 'Actions', + resources: 'Resources', + chat: 'Chat', + vaults: 'Vaults', + metrics: 'Metrics', + permissions: 'Permissions', +}; + +type Props = { + blueprint: Blueprint; + serviceId?: bigint; +}; + +const BlueprintHostCard = ({ blueprint, serviceId }: Props) => { + if (!blueprint.blueprintUi) { + return null; + } + + const { blueprintUi } = blueprint; + const publisherLabel = blueprintUi.publisher.namespace + ? `@${blueprintUi.publisher.namespace}` + : blueprintUi.publisher.name; + + return ( + +
+
+ + Hosted Blueprint Contract + + {blueprintUi.displayName} + + {blueprintUi.description} + +
+ + {blueprintUi.externalApp && ( + + )} +
+ +
+ + + + +
+ + {(blueprintUi.requestedSlug || blueprintUi.canonicalSlug) && ( +
+ + Requested host slug + + + {blueprintUi.canonicalSlug ?? blueprintUi.requestedSlug} + +
+ )} + + {blueprintUi.surfaces.length > 0 && ( +
+ + Shared host surfaces + +
+ {blueprintUi.surfaces.map((surface) => ( + + {SURFACE_LABELS[surface]} + + ))} +
+
+ )} + + {blueprintUi.externalApp && ( +
+ + Third-party app execution stays outside the shared cloud runtime + + + Tier 3 BYOdApp embedding is not enabled here. Tangle Cloud keeps the + shared host pages on-site and only hands off to publisher apps via a + new tab. + {serviceId !== undefined + ? ` Service #${serviceId.toString()} remains manageable from the hosted protocol surfaces.` + : ''} + +
+ )} +
+ ); +}; + +const InfoBlock = ({ label, value }: { label: string; value: string }) => ( +
+ + {label} + + + {value} + +
+); + +export default BlueprintHostCard; diff --git a/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/Deployment.tsx b/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/Deployment.tsx index 33ebc7644a..034657c075 100644 --- a/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/Deployment.tsx +++ b/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/Deployment.tsx @@ -5,9 +5,22 @@ import { SelectOperatorsStep } from './OperatorSelectionStep'; import { RequestArgsConfigurationStep } from './RequestArgsConfigurationStep'; import { PaymentStep } from './PaymentStep'; import { RequestModeStep } from './RequestModeStep'; +import { + Accordion, + AccordionButton, + AccordionContent, + AccordionItem, + Typography, +} from '@tangle-network/ui-components'; export const Deployment: FC = (props) => { const minimumNativeSecurityRequirement = 0; + const expectedRequestArgsCount = + props.requestSchemaFieldCount ?? + props.blueprint?.requestParams?.length ?? + 0; + const shouldShowAdvancedSummary = + expectedRequestArgsCount > 0 || props.hasRequestSchema === false; return ( <> @@ -16,9 +29,36 @@ export const Deployment: FC = (props) => { {...props} minimumNativeSecurityRequirement={minimumNativeSecurityRequirement} /> - - - + +
+ + + + Advanced configuration + + +
+ + Most services only need a name, operators, and a caller. Open + this section when you need custom request args, security + commitments, or a non-default payment setup. + + {shouldShowAdvancedSummary && ( + + {expectedRequestArgsCount > 0 + ? `This blueprint expects ${expectedRequestArgsCount} request argument${expectedRequestArgsCount === 1 ? '' : 's'}.` + : 'This blueprint has no request arguments, but its request schema could not be resolved.'} + + )} +
+ + + + +
+
+
+
); }; diff --git a/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/OperatorSelectionStep.tsx b/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/OperatorSelectionStep.tsx index 0d15276905..0609a2fb51 100644 --- a/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/OperatorSelectionStep.tsx +++ b/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/OperatorSelectionStep.tsx @@ -39,11 +39,12 @@ export const SelectOperatorsStep: FC = ({ blueprint, blueprintOperators, }) => { + const selectedOperators = watch('operators') ?? []; const [rowSelection, setRowSelection] = useState( - watch(`operators`)?.reduce((acc, operator) => { + selectedOperators.reduce((acc, operator) => { acc[operator] = true; return acc; - }, {} as RowSelectionState) || {}, + }, {} as RowSelectionState), ); const [searchQuery, setSearchQuery] = useState(''); @@ -105,6 +106,29 @@ export const SelectOperatorsStep: FC = ({ setValue(`operators`, Object.keys(rowSelection) as Address[]); }, [rowSelection, setValue]); + useEffect(() => { + if (selectedOperators.length === 0) { + return; + } + + const nextSelection = selectedOperators.reduce((acc, operator) => { + acc[operator] = true; + return acc; + }, {} as RowSelectionState); + + const currentKeys = Object.keys(rowSelection).filter( + (key) => rowSelection[key], + ); + const nextKeys = Object.keys(nextSelection); + const isSame = + currentKeys.length === nextKeys.length && + nextKeys.every((key) => rowSelection[key]); + + if (!isSame) { + setRowSelection(nextSelection); + } + }, [rowSelection, selectedOperators]); + const onSelectAsset = (asset: StakingAsset, isChecked: boolean) => { const selectedAssets_ = Array.from(selectedAssets ?? []); const newSelectedAssets = isChecked diff --git a/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/PaymentStep.tsx b/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/PaymentStep.tsx index cc24e91f88..9ca5db65c7 100644 --- a/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/PaymentStep.tsx +++ b/apps/tangle-cloud/src/pages/blueprints/[id]/deploy/DeploySteps/PaymentStep.tsx @@ -41,6 +41,8 @@ export const PaymentStep: FC = ({ setValue('paymentAmount', nextValue); }; + const selectedPaymentAssetId = watch('paymentAsset')?.id; + return ( @@ -56,6 +58,7 @@ export const PaymentStep: FC = ({ Select Payment Asset + updateForm('uiDraft', { ...form.uiDraft, requestedSlug: v }) + } + placeholder="trading" + isControlled + /> + +
+ + Publisher namespace + + + updateForm('uiDraft', { + ...form.uiDraft, + publisherNamespace: v, + }) + } + placeholder="tangle or your project" + isControlled + /> +
+ + +
+
+ + Service label + + + updateForm('uiDraft', { ...form.uiDraft, serviceNoun: v }) + } + placeholder="service" + isControlled + /> +
+
+ + Resource label + + + updateForm('uiDraft', { ...form.uiDraft, resourceNoun: v }) + } + placeholder="bot" + isControlled + /> +
+
+ + Resource route + + +
+
+ +
+ + Host surfaces + +
+ {BLUEPRINT_UI_SURFACE_OPTIONS.map((option) => { + const isSelected = form.uiDraft.surfaces.includes(option.value); + + return ( + + ); + })} +
+
+ +
+
+ + External app URL + + + updateForm('uiDraft', { ...form.uiDraft, externalAppUrl: v }) + } + placeholder="https://app.example.com" + isControlled + /> +
+
+ + External app mode + + +
+
+ +
+ + Metadata JSON preview + +