Frontend[UI]: Layout with global components#57
Frontend[UI]: Layout with global components#57JoelVR17 merged 1 commit intoTrustless-Work:developfrom
Conversation
📝 WalkthroughWalkthroughThis pull request introduces a new ROI (Return on Investment) feature for the investor tokenization application. It adds a complete ROI module with layout, page, multiple UI components, type definitions, and mock data. Additionally, the root layout is updated for light mode with suppressed hydration warnings, the navigation bar includes a new ROI link, and a planning document outlines a future wallet kit migration. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant RoiPage as ROI Page
participant RoiHeader as ROI Header
participant CampaignToolbar as Campaign Toolbar
participant CampaignList as Campaign List
participant CampaignCard as Campaign Card
participant ClaimButton as Claim ROI Button
User->>RoiPage: Visit /roi page
RoiPage->>RoiHeader: Render with search state
RoiPage->>CampaignToolbar: Render with filter state
User->>RoiHeader: Enter search text
RoiHeader->>RoiPage: Update searchValue
RoiPage->>RoiPage: Recompute filtered campaigns
User->>CampaignToolbar: Change status filter
CampaignToolbar->>RoiPage: Update filterValue
RoiPage->>RoiPage: Recompute filtered campaigns
RoiPage->>CampaignList: Render with filtered campaigns
CampaignList->>CampaignCard: Render each campaign
CampaignCard->>ClaimButton: Render with campaignId
User->>ClaimButton: Click Claim ROI
ClaimButton->>RoiPage: Invoke handleClaimRoi callback
RoiPage->>RoiPage: Log claimed campaign ID
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (4)
apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts (1)
1-1: Use the@/alias for this local import.This relative import bypasses the frontend import convention and makes later moves/renames noisier than they need to be. As per coding guidelines, "Use path alias
@/*mapping to./src/*for imports".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts` at line 1, Replace the relative import in mock-campaigns.ts with the project path-alias: change the "../types/campaign.types" import to use the "@/…" alias that maps to ./src, e.g. import the Campaign type from "@/features/roi/types/campaign.types" (update the import statement in the file that currently references Campaign).apps/investor-tokenization/src/features/roi/components/campaign-card.tsx (1)
15-23: Hardcoded locale in currency formatting.The
formatMinInvestfunction hardcodes"en-US"locale. Consider usingundefinedto letIntl.NumberFormatuse the user's default locale, or accept locale as a parameter for flexibility.💡 Optional locale-aware formatting
-function formatMinInvest(cents: number, currency: string): string { +function formatMinInvest(cents: number, currency: string, locale?: string): string { const value = cents / 100; - return new Intl.NumberFormat("en-US", { + return new Intl.NumberFormat(locale, { style: "currency", currency, minimumFractionDigits: 0, maximumFractionDigits: 0, }).format(value); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/features/roi/components/campaign-card.tsx` around lines 15 - 23, The currency formatter in function formatMinInvest hardcodes the "en-US" locale; change it to use the runtime/default locale or accept a locale parameter. Update formatMinInvest(cents: number, currency: string[, locale?: string]) to call new Intl.NumberFormat(locale ?? undefined, { style: "currency", currency, minimumFractionDigits: 0, maximumFractionDigits: 0 }) so it uses the user's locale when none is provided (or thread through a locale argument from callers).apps/investor-tokenization/src/features/roi/components/campaign-list.tsx (1)
9-19: Consider adding an empty state.When
campaignsis empty, the component renders an empty<ul>. Consider showing a user-friendly message when no campaigns match the current filters.💡 Optional empty state handling
export function CampaignList({ campaigns, onClaimRoi }: CampaignListProps) { + if (campaigns.length === 0) { + return ( + <p className="text-center text-muted-foreground py-8"> + No campaigns found. + </p> + ); + } + return ( <ul className="flex flex-col gap-4 list-none p-0 m-0">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/features/roi/components/campaign-list.tsx` around lines 9 - 19, The CampaignList currently renders an empty <ul> when the campaigns prop is empty; update the CampaignList component to detect when campaigns.length === 0 (or !campaigns || campaigns.length === 0) and render a user-friendly empty state instead of an empty list — e.g., return a centered message or an <EmptyState /> placeholder with guidance like "No campaigns match your filters" while preserving the existing list rendering for non-empty arrays; reference the CampaignList function and the campaigns prop to locate where to add this conditional rendering.apps/investor-tokenization/src/app/roi/page.tsx (1)
23-25: Simplify filtering onceCampaignFiltervalues are aligned.The
.toLowerCase()call is necessary due to the case mismatch between filter values andCampaignStatus. Once the filter values are updated to uppercase (per the comment oncampaign-filter.tsx), this can be simplified:♻️ Proposed simplification (after filter fix)
if (filter !== "all") { - list = list.filter((c) => c.status.toLowerCase() === filter); + list = list.filter((c) => c.status === filter); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/investor-tokenization/src/app/roi/page.tsx` around lines 23 - 25, The current filter compares c.status.toLowerCase() to filter to work around casing; once CampaignFilter values are updated to match CampaignStatus (uppercase per campaign-filter.tsx), simplify the condition in apps/investor-tokenization/src/app/roi/page.tsx by removing .toLowerCase() and compare c.status === filter (i.e., update the list.filter callback that currently reads list.filter((c) => c.status.toLowerCase() === filter) to use direct equality), ensuring the 'filter' variable and CampaignStatus enums are the same case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx`:
- Around line 32-36: The SelectTrigger component's hardcoded "text-black" in its
className overrides dark mode; update the class string passed to SelectTrigger
(the cn(...) call) to replace "text-black" with "text-foreground" so it respects
theme colors, keeping the rest of the classes and the existing cn(className)
composition intact.
- Around line 12-17: filterOptions uses lowercase values that don't match the
CampaignStatus union, breaking type-safety; update the values in filterOptions
(the array declared as filterOptions) so the campaign state entries use the
canonical uppercase strings "READY", "PENDING", "CLOSED" while keeping the "all"
option as-is, and then update the filter usage to compare directly via c.status
=== filter (and ensure the filter variable/type is CampaignStatus | "all") so
you no longer need .toLowerCase().
In
`@apps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsx`:
- Around line 5-21: The badge always renders a green check for every state and
accepts a plain string; update CampaignStatusBadge to be status-aware by
defining a CampaignStatus union type (e.g., "READY" | "PENDING" | "CLOSED") and
change CampaignStatusBadgeProps.status to that type, then compute the badge
appearance inside CampaignStatusBadge (use a switch or a mapping keyed by
status) to return appropriate class names and icon components (e.g., green Check
for READY, yellow Clock/Spinner for PENDING, gray X/Lock for CLOSED) and merge
those classes with the existing cn call and className prop; ensure you provide a
safe default/fallback for unexpected values and update any imports/usages of
CampaignStatusBadge accordingly.
In `@apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx`:
- Around line 13-15: The ROI_NAV_ITEMS entry has a label/destination mismatch:
the item { href: "/", label: "Manage Campaigns", icon: Megaphone } should point
to the actual Manage Campaigns route or its correct label; update ROI_NAV_ITEMS
so the label matches the href (either change href to the Manage Campaigns route
path used elsewhere or change label to "Invest"), e.g. locate ROI_NAV_ITEMS in
roi-dashboard-shell.tsx and adjust the first entry's href or label so that the
label "Manage Campaigns" leads to the Manage Campaigns route and "/" remains
labeled "Invest" consistently.
- Around line 24-25: The ROI shell is being mounted as a fullscreen overlay due
to the outer div's "fixed inset-0 z-[60]" class (and a similar block at lines
47-50), which leaves global header/dock underneath and prevents internal
scrolling; remove or replace the positioning so the ROI route renders in normal
flow (e.g., drop "fixed inset-0 z-[60]" and use a non-fixed container like
"relative" or no positioning) or, if you intend it to own chrome, make it a
full-page layout that also accounts for header/dock offsets and provides an
internal scrollable container (add a wrapper with explicit top/bottom offsets
and overflow-auto). Update the div wrapping SidebarProvider (and the duplicate
block) to ensure content can scroll when taller than the viewport and that
global header/dock are either covered intentionally or remain visible as part of
the app chrome.
In `@apps/investor-tokenization/src/features/roi/types/campaign.types.ts`:
- Around line 1-10: The CampaignStatus union in this file is out-of-sync with
the canonical domain enum; update the CampaignStatus type to use the domain
values DRAFT | ACTIVE | FUNDED | PAUSED | CLOSED (and replace the current
"READY" and "PENDING") so the Campaign type accepts real payloads, and ensure
any local references to CampaignStatus (e.g., in the Campaign type or UI
conversion helpers) are adjusted or mapped to the canonical values instead of
the old READY/PENDING tokens.
In `@docs/PLAN_WALLET_KIT.md`:
- Around line 61-63: The fenced code block containing "❌ Missing initial setup.
Run 'trustless-work init'..." should be tagged with a language to satisfy
markdownlint MD040; update the opening fence from ``` to ```text in the block in
PLAN_WALLET_KIT.md so the example remains unchanged but the linter error is
resolved.
---
Nitpick comments:
In `@apps/investor-tokenization/src/app/roi/page.tsx`:
- Around line 23-25: The current filter compares c.status.toLowerCase() to
filter to work around casing; once CampaignFilter values are updated to match
CampaignStatus (uppercase per campaign-filter.tsx), simplify the condition in
apps/investor-tokenization/src/app/roi/page.tsx by removing .toLowerCase() and
compare c.status === filter (i.e., update the list.filter callback that
currently reads list.filter((c) => c.status.toLowerCase() === filter) to use
direct equality), ensuring the 'filter' variable and CampaignStatus enums are
the same case.
In `@apps/investor-tokenization/src/features/roi/components/campaign-card.tsx`:
- Around line 15-23: The currency formatter in function formatMinInvest
hardcodes the "en-US" locale; change it to use the runtime/default locale or
accept a locale parameter. Update formatMinInvest(cents: number, currency:
string[, locale?: string]) to call new Intl.NumberFormat(locale ?? undefined, {
style: "currency", currency, minimumFractionDigits: 0, maximumFractionDigits: 0
}) so it uses the user's locale when none is provided (or thread through a
locale argument from callers).
In `@apps/investor-tokenization/src/features/roi/components/campaign-list.tsx`:
- Around line 9-19: The CampaignList currently renders an empty <ul> when the
campaigns prop is empty; update the CampaignList component to detect when
campaigns.length === 0 (or !campaigns || campaigns.length === 0) and render a
user-friendly empty state instead of an empty list — e.g., return a centered
message or an <EmptyState /> placeholder with guidance like "No campaigns match
your filters" while preserving the existing list rendering for non-empty arrays;
reference the CampaignList function and the campaigns prop to locate where to
add this conditional rendering.
In `@apps/investor-tokenization/src/features/roi/data/mock-campaigns.ts`:
- Line 1: Replace the relative import in mock-campaigns.ts with the project
path-alias: change the "../types/campaign.types" import to use the "@/…" alias
that maps to ./src, e.g. import the Campaign type from
"@/features/roi/types/campaign.types" (update the import statement in the file
that currently references Campaign).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 23bbe689-76f9-43aa-970b-22b9036ba90f
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (17)
apps/investor-tokenization/src/app/layout.tsxapps/investor-tokenization/src/app/roi/layout.tsxapps/investor-tokenization/src/app/roi/page.tsxapps/investor-tokenization/src/components/shared/Navbar.tsxapps/investor-tokenization/src/features/roi/components/campaign-card.tsxapps/investor-tokenization/src/features/roi/components/campaign-filter.tsxapps/investor-tokenization/src/features/roi/components/campaign-list.tsxapps/investor-tokenization/src/features/roi/components/campaign-search.tsxapps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsxapps/investor-tokenization/src/features/roi/components/campaign-toolbar.tsxapps/investor-tokenization/src/features/roi/components/claim-roi-button.tsxapps/investor-tokenization/src/features/roi/components/investor-profile-card.tsxapps/investor-tokenization/src/features/roi/components/roi-header.tsxapps/investor-tokenization/src/features/roi/data/mock-campaigns.tsapps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsxapps/investor-tokenization/src/features/roi/types/campaign.types.tsdocs/PLAN_WALLET_KIT.md
| const filterOptions = [ | ||
| { value: "all", label: "All Campaigns" }, | ||
| { value: "ready", label: "Ready" }, | ||
| { value: "pending", label: "Pending" }, | ||
| { value: "closed", label: "Closed" }, | ||
| ]; |
There was a problem hiding this comment.
Filter values don't match CampaignStatus type.
The filter options use lowercase values ("ready", "pending", "closed") while CampaignStatus is defined as "READY" | "PENDING" | "CLOSED". This inconsistency forces the page to use .toLowerCase() for comparison and loses type safety.
Consider aligning with the canonical type:
🔧 Proposed fix to use uppercase values
const filterOptions = [
{ value: "all", label: "All Campaigns" },
- { value: "ready", label: "Ready" },
- { value: "pending", label: "Pending" },
- { value: "closed", label: "Closed" },
+ { value: "READY", label: "Ready" },
+ { value: "PENDING", label: "Pending" },
+ { value: "CLOSED", label: "Closed" },
];This allows the page to filter directly: c.status === filter without case conversion.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx`
around lines 12 - 17, filterOptions uses lowercase values that don't match the
CampaignStatus union, breaking type-safety; update the values in filterOptions
(the array declared as filterOptions) so the campaign state entries use the
canonical uppercase strings "READY", "PENDING", "CLOSED" while keeping the "all"
option as-is, and then update the filter usage to compare directly via c.status
=== filter (and ensure the filter variable/type is CampaignStatus | "all") so
you no longer need .toLowerCase().
| <SelectTrigger | ||
| className={cn( | ||
| "w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-black", | ||
| className | ||
| )} |
There was a problem hiding this comment.
Hardcoded text-black may break dark mode.
The text-black class will render black text regardless of the theme. Use text-foreground instead to respect the theme's color scheme.
🎨 Proposed fix
<SelectTrigger
className={cn(
- "w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-black",
+ "w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-foreground",
className
)}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <SelectTrigger | |
| className={cn( | |
| "w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-black", | |
| className | |
| )} | |
| <SelectTrigger | |
| className={cn( | |
| "w-[180px] rounded-lg border border-input bg-background h-9 text-sm font-medium text-foreground", | |
| className | |
| )} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/investor-tokenization/src/features/roi/components/campaign-filter.tsx`
around lines 32 - 36, The SelectTrigger component's hardcoded "text-black" in
its className overrides dark mode; update the class string passed to
SelectTrigger (the cn(...) call) to replace "text-black" with "text-foreground"
so it respects theme colors, keeping the rest of the classes and the existing
cn(className) composition intact.
| type CampaignStatusBadgeProps = { | ||
| status: string; | ||
| className?: string; | ||
| }; | ||
|
|
||
| export function CampaignStatusBadge({ status, className }: CampaignStatusBadgeProps) { | ||
| return ( | ||
| <Badge | ||
| className={cn( | ||
| "rounded-md bg-emerald-500 text-white border-0 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide gap-1 [&_svg]:size-3", | ||
| className | ||
| )} | ||
| > | ||
| <Check className="size-3" /> | ||
| {status} | ||
| </Badge> | ||
| ); |
There was a problem hiding this comment.
Badge doesn't differentiate between campaign statuses.
The badge always renders as green with a check icon regardless of whether the status is "READY", "PENDING", or "CLOSED". This creates a misleading UX where pending/closed campaigns appear as ready.
Additionally, status is typed as string instead of CampaignStatus, losing type safety.
🔧 Proposed fix with status-aware styling
+import type { CampaignStatus } from "../types/campaign.types";
+import { Clock, Check, XCircle } from "lucide-react";
+
+const statusConfig: Record<CampaignStatus, { icon: typeof Check; className: string }> = {
+ READY: { icon: Check, className: "bg-emerald-500 text-white" },
+ PENDING: { icon: Clock, className: "bg-yellow-500 text-white" },
+ CLOSED: { icon: XCircle, className: "bg-gray-500 text-white" },
+};
+
type CampaignStatusBadgeProps = {
- status: string;
+ status: CampaignStatus;
className?: string;
};
export function CampaignStatusBadge({ status, className }: CampaignStatusBadgeProps) {
+ const config = statusConfig[status];
+ const Icon = config.icon;
+
return (
<Badge
className={cn(
- "rounded-md bg-emerald-500 text-white border-0 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide gap-1 [&_svg]:size-3",
+ "rounded-md border-0 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide gap-1 [&_svg]:size-3",
+ config.className,
className
)}
>
- <Check className="size-3" />
+ <Icon className="size-3" />
{status}
</Badge>
);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/investor-tokenization/src/features/roi/components/campaign-status-badge.tsx`
around lines 5 - 21, The badge always renders a green check for every state and
accepts a plain string; update CampaignStatusBadge to be status-aware by
defining a CampaignStatus union type (e.g., "READY" | "PENDING" | "CLOSED") and
change CampaignStatusBadgeProps.status to that type, then compute the badge
appearance inside CampaignStatusBadge (use a switch or a mapping keyed by
status) to return appropriate class names and icon components (e.g., green Check
for READY, yellow Clock/Spinner for PENDING, gray X/Lock for CLOSED) and merge
those classes with the existing cn call and className prop; ensure you provide a
safe default/fallback for unexpected values and update any imports/usages of
CampaignStatusBadge accordingly.
| const ROI_NAV_ITEMS = [ | ||
| { href: "/", label: "Manage Campaigns", icon: Megaphone }, | ||
| { href: "/roi", label: "ROI", icon: TrendingUp }, |
There was a problem hiding this comment.
Fix the sidebar label/destination mismatch.
href="/" routes back to the investor landing page, but this item is labeled "Manage Campaigns". In this app, / is already surfaced as "Invest" in apps/investor-tokenization/src/components/shared/Navbar.tsx, so the label currently points users to a different destination than it describes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx` around
lines 13 - 15, The ROI_NAV_ITEMS entry has a label/destination mismatch: the
item { href: "/", label: "Manage Campaigns", icon: Megaphone } should point to
the actual Manage Campaigns route or its correct label; update ROI_NAV_ITEMS so
the label matches the href (either change href to the Manage Campaigns route
path used elsewhere or change label to "Invest"), e.g. locate ROI_NAV_ITEMS in
roi-dashboard-shell.tsx and adjust the first entry's href or label so that the
label "Manage Campaigns" leads to the Manage Campaigns route and "/" remains
labeled "Invest" consistently.
| <div className="fixed inset-0 z-[60] flex min-h-svh bg-background"> | ||
| <SidebarProvider> |
There was a problem hiding this comment.
Avoid mounting the ROI shell as a fullscreen overlay.
fixed inset-0 z-[60] turns this route layout into an overlay on top of the root layout instead of normal page content. That leaves the global header/dock mounted underneath, and because there is no inner scroll container here, ROI content taller than the viewport will become unreachable. This should either render in normal flow or own both the route chrome and scrolling.
Also applies to: 47-50
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/investor-tokenization/src/features/roi/roi-dashboard-shell.tsx` around
lines 24 - 25, The ROI shell is being mounted as a fullscreen overlay due to the
outer div's "fixed inset-0 z-[60]" class (and a similar block at lines 47-50),
which leaves global header/dock underneath and prevents internal scrolling;
remove or replace the positioning so the ROI route renders in normal flow (e.g.,
drop "fixed inset-0 z-[60]" and use a non-fixed container like "relative" or no
positioning) or, if you intend it to own chrome, make it a full-page layout that
also accounts for header/dock offsets and provides an internal scrollable
container (add a wrapper with explicit top/bottom offsets and overflow-auto).
Update the div wrapping SidebarProvider (and the duplicate block) to ensure
content can scroll when taller than the viewport and that global header/dock are
either covered intentionally or remain visible as part of the app chrome.
| export type CampaignStatus = "READY" | "PENDING" | "CLOSED"; | ||
|
|
||
| export type Campaign = { | ||
| id: string; | ||
| title: string; | ||
| description: string; | ||
| status: CampaignStatus; | ||
| loansCompleted: number; | ||
| minInvestCents: number; | ||
| currency: string; |
There was a problem hiding this comment.
Align CampaignStatus with the domain enum.
The ROI feature introduces "READY" | "PENDING" | "CLOSED", but the shared Campaign model uses DRAFT | ACTIVE | FUNDED | PAUSED | CLOSED. The first real payload with ACTIVE, FUNDED, or PAUSED will not fit this type, so either reuse the canonical enum values here or add an explicit UI-mapping layer on top of them. Based on learnings, Campaign model should have status enum with values: DRAFT, ACTIVE, FUNDED, PAUSED, CLOSED.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/investor-tokenization/src/features/roi/types/campaign.types.ts` around
lines 1 - 10, The CampaignStatus union in this file is out-of-sync with the
canonical domain enum; update the CampaignStatus type to use the domain values
DRAFT | ACTIVE | FUNDED | PAUSED | CLOSED (and replace the current "READY" and
"PENDING") so the Campaign type accepts real payloads, and ensure any local
references to CampaignStatus (e.g., in the Campaign type or UI conversion
helpers) are adjusted or mapped to the canonical values instead of the old
READY/PENDING tokens.
| ``` | ||
| ❌ Missing initial setup. Run 'trustless-work init' first to install dependencies and create .twblocks.json (uiBase). | ||
| ``` |
There was a problem hiding this comment.
Add a language to the fenced error example.
This block currently trips markdownlint (MD040). Tagging it as text keeps the doc lint-clean without changing the content.
📝 Proposed fix
- ```
+ ```text
❌ Missing initial setup. Run 'trustless-work init' first to install dependencies and create .twblocks.json (uiBase).
```📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| ❌ Missing initial setup. Run 'trustless-work init' first to install dependencies and create .twblocks.json (uiBase). | |
| ``` |
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 61-61: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/PLAN_WALLET_KIT.md` around lines 61 - 63, The fenced code block
containing "❌ Missing initial setup. Run 'trustless-work init'..." should be
tagged with a language to satisfy markdownlint MD040; update the opening fence
from ``` to ```text in the block in PLAN_WALLET_KIT.md so the example remains
unchanged but the linter error is resolved.
Creating PR for layout with global components.
Summary by CodeRabbit
Release Notes
New Features
Documentation