Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .devcontainer/constructs/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "Constructs",
"image": "mcr.microsoft.com/dotnet/sdk:9.0",
"features": {},
"workspaceFolder": "/workspaces/Ivy-Examples/constructs",
"postCreateCommand": "dotnet tool install -g Ivy.Console",
"postStartCommand": "dotnet watch",
"forwardPorts": [5010],
"portsAttributes": {
"5010": {
"label": "Constructs",
"onAutoForward": "openPreview"
}
},
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csharp",
"ms-vscode.powershell",
"ms-dotnettools.csdevkit"
]
},
"codespaces": {
"openFiles": ["Apps/ConstructsApp.cs"]
}
}
}
1 change: 0 additions & 1 deletion constructs/.nvmrc

This file was deleted.

44 changes: 22 additions & 22 deletions constructs/Apps/ConstructsApp.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Constructs;
using Ivy.Examples.Constructs.Domain;
using Ivy.Examples.Constructs.Extensions;
using ConstructsExample.Domain;
using ConstructsExample.Extensions;

namespace Ivy.Examples.Constructs.Apps;
namespace ConstructsExample.Apps;

[App(icon: Icons.PartyPopper, title: "Constructs (AWS)")]
[App(icon: Icons.FileCode2, title: "Constructs")]
public class ConstructsApp : ViewBase
{
private RootConstruct? _root;
Expand All @@ -16,13 +16,13 @@ public class ConstructsApp : ViewBase
var parent = UseState(string.Empty);
var childId = UseState(string.Empty);
var maxLines = UseState(MaxLines);
var client = UseService<IClientProvider>();

if (_root is null)
{
try
{
_root = DemoConstruct.BuildRoot();
parent.Set(_root.Node.Path);
}
catch (Exception ex)
{
Expand All @@ -47,8 +47,7 @@ public class ConstructsApp : ViewBase
LayoutView left = Layout.Vertical().Gap(6)
| Text.H2("AWS Constructs — interactive demo")
| Text.Block("Write a parent path, add a child construct, or reset to the canonical tree.")
| Text.Block("Parent path, ex. Root/Demo/Nested. Start typing to filter tree view.")
| parent.ToInput(placeholder: "Root")
| parent.ToInput(placeholder: "Parent path, ex. Root/Demo/Nested. Start typing to filter tree view.")
| Text.Block("New child id")
| childId.ToInput(placeholder: "ChildX")
| (
Expand All @@ -62,35 +61,37 @@ public class ConstructsApp : ViewBase
string newChildId = childId.Value.Trim();
if (string.IsNullOrWhiteSpace(newChildId))
{
status.Set("Enter a non-empty child id.");
client.Toast("Enter a non-empty child id.");
return;
}

Construct? parentNode = _root.FindByPath(parentPath);
if (parentNode == null)
{
status.Set($"Parent path not found: {parentPath}");
client.Toast($"Parent path not found: {parentPath}");
return;
}

_ = new Construct(parentNode, newChildId);
childId.Set(string.Empty);
status.Set($"Added: {parentNode.Node.Path}/{newChildId}");
})
| new Button("Reset", onClick: () =>
client.Toast($"Added: {parentNode.Node.Path}/{newChildId}");
}).Icon(Icons.Plus)
| new Button("Reset", _ =>
{
_root = DemoConstruct.BuildRoot();
parent.Set(_root.Node.Path);
status.Set("Tree reset.");
parent.Set(string.Empty);
client.Toast("Tree reset.");
maxLines.Set(MaxLines);
})
)
| (string.IsNullOrWhiteSpace(status.Value) ? Text.Block(string.Empty) : Text.Block(status.Value));
}).Icon(Icons.RefreshCw)
)
| Text.Small("This demo uses the AWS Constructs library to build composable configuration models.")
| Text.Markdown("Built with [Ivy Framework](https://github.com/Ivy-Interactive/Ivy-Framework) and [AWS Constructs](https://github.com/aws/constructs)")
;

// Build right (tree view) panel
Card right = new Card(
Layout.Vertical().Gap(6).Padding(3)
| Text.Block("Current tree (subtree of the selected parent)")
Layout.Vertical().Gap(3).Padding(3)
| Text.H4("Current tree")
| new Separator()
| Text.Markdown("```text\n" + string.Join('\n', visible) + "\n```")
| (hasMore
Expand All @@ -99,14 +100,13 @@ public class ConstructsApp : ViewBase
? new Button("Collapse", onClick: () => maxLines.Set(MaxLines))
: Text.Block(string.Empty)))
)
.Width(Size.Units(180).Max(900));
.Width(Size.Fraction(0.8f));

return Layout.Center()
| new Card(
Layout.Horizontal().Gap(12).Padding(3)
| left
| right
)
.Width(Size.Units(340).Max(1000));
).Width(Size.Fraction(0.8f));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoWarn>CS8618;CS8603;CS8602;CS8604;CS9113</NoWarn>
<RootNamespace>Ivy.Examples.Constructs</RootNamespace>
<RootNamespace>ConstructsExample</RootNamespace>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Constructs", "Ivy.Examples.Constructs.csproj", "{039638F3-A6D7-FB67-E733-27BD896EFDC4}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConstructsExample", "ConstructsExample.csproj", "{039638F3-A6D7-FB67-E733-27BD896EFDC4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
10 changes: 5 additions & 5 deletions constructs/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ ARG BUILD_CONFIGURATION=Release
WORKDIR /src

# Copy and restore
COPY ["Ivy.Examples.Constructs.csproj", "./"]
RUN dotnet restore "Ivy.Examples.Constructs.csproj"
COPY ["ConstructsExample.csproj", "./"]
RUN dotnet restore "ConstructsExample.csproj"

# Copy everything and build
COPY . .
RUN dotnet build "Ivy.Examples.Constructs.csproj" -c $BUILD_CONFIGURATION -o /app/build
RUN dotnet build "ConstructsExample.csproj" -c $BUILD_CONFIGURATION -o /app/build

# Publish stage
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "Ivy.Examples.Constructs.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=true
RUN dotnet publish "ConstructsExample.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=true

# Final runtime image
FROM base AS final
Expand All @@ -31,4 +31,4 @@ ENV PORT=80
ENV ASPNETCORE_URLS="http://+:80"

# Run the executable
ENTRYPOINT ["dotnet","./Ivy.Examples.Constructs.dll"]
ENTRYPOINT ["dotnet","./ConstructsExample.dll"]
2 changes: 1 addition & 1 deletion constructs/Domain/DemoConstruct.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Constructs;

namespace Ivy.Examples.Constructs.Domain;
namespace ConstructsExample.Domain;

/// <summary>
/// Represents a demonstration construct that builds a small construct tree for examples and tests.
Expand Down
2 changes: 1 addition & 1 deletion constructs/Domain/Traversal.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Constructs;

namespace Ivy.Examples.Constructs.Domain;
namespace ConstructsExample.Domain;

/// <summary>
/// Provides helpers for traversing and flattening a tree of <see cref="Construct"/> instances.
Expand Down
2 changes: 1 addition & 1 deletion constructs/Extensions/ConstructExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Constructs;

namespace Ivy.Examples.Constructs.Extensions
namespace ConstructsExample.Extensions
{
/// <summary>
/// Provides extension methods for working with <see cref="Construct"/> instances.
Expand Down
2 changes: 1 addition & 1 deletion constructs/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@
global using System.Globalization;
global using System.Reactive.Linq;

namespace Ivy.Examples.Constructs;
namespace ConstructsExample;
16 changes: 14 additions & 2 deletions constructs/Program.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
using Ivy.Examples.Constructs.Apps;
using ConstructsExample.Apps;
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US");
var server = new Server();
#if DEBUG
server.UseHotReload();
#endif
server.AddAppsFromAssembly();
server.AddConnectionsFromAssembly();
var chromeSettings = new ChromeSettings().DefaultApp<ConstructsApp>().UseTabs(preventDuplicates: true);
var customHeader = Layout.Vertical().Gap(2).Align(Align.Center)
| new Html(@"
<div>
<a href=""https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=Ivy-Interactive%2FIvy-Examples&machine=standardLinux32gb&devcontainer_path=.devcontainer%2Fconstructs%2Fdevcontainer.json&location=EuropeWest"">
<img src=""https://github.com/codespaces/badge.svg"" alt=""Open Constructs in Codespaces"" />
</a>
</div>
")
| new Button("Source Code").Url("https://github.com/Ivy-Interactive/Ivy-Examples/tree/main/constructs").Icon(Icons.ExternalLink).Width(Size.Full()).Height(Size.Units(10));
var chromeSettings = new ChromeSettings()
.DefaultApp<ConstructsApp>()
.UseTabs(preventDuplicates: true)
.Header(customHeader);
server.UseChrome(chromeSettings);
await server.RunAsync();
134 changes: 55 additions & 79 deletions constructs/README.md
Original file line number Diff line number Diff line change
@@ -1,100 +1,76 @@
# Ivy Example: AWS Constructs
# AWS Constructs

An interactive Ivy demo showcasing the basics of the [`Constructs`](https://github.com/aws/constructs) .NET package inside a pure C# Ivy app: build a small construct tree, inspect node paths, and add child nodes at runtime.
<img width="1919" height="913" alt="Image" src="https://github.com/user-attachments/assets/a813ff66-7909-43ed-8df8-2e7c04683acd" />

---
## One-Click Development Environment

## Requirements
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=Ivy-Interactive%2FIvy-Examples&machine=standardLinux32gb&devcontainer_path=.devcontainer%2Fconstructs%2Fdevcontainer.json&location=EuropeWest)

- **.NET 9 SDK**
- **Node.js 20 LTS** for **Constructs/jsii**
The **Constructs** .NET package is built with **jsii** and launches a Node.js child process.
Click the badge above to open Ivy Examples repository in GitHub Codespaces with:
- **.NET 9.0** SDK pre-installed
- **Ready-to-run** development environment
- **No local setup** required

---
## Created Using Ivy

## Run
Web application created using [Ivy-Framework](https://github.com/Ivy-Interactive/Ivy-Framework).

From this `constructs/` folder:
**Ivy** - The ultimate framework for building internal tools with LLM code generation by unifying front-end and back-end into a single C# codebase. With Ivy, you can build robust internal tools and dashboards using C# and AI assistance based on your existing database.

```bash
dotnet restore
dotnet watch
# Open the printed URL (e.g., http://localhost:5010)
```
Ivy is a web framework for building interactive web applications using C# and .NET.

If you see jsii/Node warnings or `$jsii.byref` errors, switch to **Node 20/22 LTS** and restart. That requirement comes from **Constructs/jsii**.
## Interactive Example For AWS Constructs

---
This example demonstrates building composable configuration models using the [AWS Constructs library](https://github.com/aws/constructs) integrated with Ivy. Constructs are classes that define "pieces of system state" and can be composed together to form higher-level building blocks.

## What this demo shows
**What This Application Does:**

- **RootConstruct** as the root (no parent).
- **DemoConstruct** builds a canonical tree:
- **Build Construct Trees**: Create hierarchical construct structures with parent-child relationships
- **Interactive Tree Navigation**: Filter and view subtrees by specifying parent paths
- **Dynamic Node Addition**: Add new child constructs at runtime with custom IDs
- **ASCII Tree Visualization**: View the construct tree structure in a clean ASCII format
- **Tree Management**: Reset to canonical structure or expand/collapse large trees

```
/Root
└─ Demo
├─ ChildA
├─ ChildB
└─ Nested
├─ Leaf1
└─ Leaf2
```
**Technical Implementation:**

- **Interactive actions**
- Uses AWS Constructs .NET package with jsii bridge to Node.js runtime
- Implements `RootConstruct` as the root node with no parent
- Creates canonical tree structure with Demo, ChildA, ChildB, and Nested constructs
- Provides interactive UI for tree manipulation and visualization
- Single C# view (`Apps/ConstructsApp.cs`) built with Ivy UI primitives

- Write **Parent path** (e.g., `Root`, `/Root/Demo/Nested`).
- **Add child** with any id (`new Construct(parent, id)`).
- **Reset** to the canonical tree.
- Compact **ASCII subtree** view with “Show more / Collapse”.
## How to Run

---
1. **Prerequisites**: .NET 9.0 SDK
2. **Navigate to the example**:
```bash
cd constructs
```
3. **Restore dependencies**:
```bash
dotnet restore
```
4. **Run the application**:
```bash
dotnet watch
```
5. **Open your browser** to the URL shown in the terminal (typically `http://localhost:5010`)

## UI overview
## How to Deploy

- **Left**: Controls (parent input, new child id, Add, Reset, status)
- **Right**: Current tree (ASCII, limited to N lines)
Deploy this example to Ivy's hosting platform:

![screenshot](screenshot.jpg)
1. **Navigate to the example**:
```bash
cd constructs
```
2. **Deploy to Ivy hosting**:
```bash
ivy deploy
```
This will deploy your AWS Constructs interactive demo with a single command.

---
## Learn More

## Project structure

```
constructs/
├─ Apps/
│ └─ ConstructsApp.cs
├─ Domain/
│ ├─ Traversal.cs
│ └─ DemoConstruct.cs
├─ Extensions/
│ └─ ConstructExtensions.cs
├─ README.md
├─ screenshot.jpg
└─ Ivy.Examples.Constructs.csproj
```

Namespaces follow `Ivy.Examples.Constructs.*`. The app is registered with `[App(...)]` in `ConstructsApp.cs`.

---

## Why Node.js is needed (jsii)

The **Constructs** .NET package is generated via **jsii** and talks to a **Node.js** runtime under the hood. Ivy does not require Node, but when you use jsii-based packages (like Constructs), the jsii bridge spins up a Node child process. That’s why **Node 20 LTS** is mandatory here.

---

## Troubleshooting

- **Infinite spinner / `$jsii.byref` / init failed**
Use **Node 20 LTS**, then:

```bash
dotnet clean
dotnet restore
dotnet watch
```

- **Long tree output**
Select a deeper parent path (subtree view) and use “Show more / Collapse”.
- AWS Constructs GitHub repository: [github.com/aws/constructs](https://github.com/aws/constructs)
- Ivy Documentation: [docs.ivy.app](https://docs.ivy.app)
Binary file removed constructs/screenshot.jpg
Binary file not shown.