Skip to content
Draft
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
4 changes: 4 additions & 0 deletions .cursor/environment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "DevCentr",
"install": "bash .cursor/install.sh"
}
44 changes: 44 additions & 0 deletions .cursor/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# Cloud Agent bootstrap for the DevCentr monorepo.
#
# Installs everything needed to build and run the D desktop app (under app/)
# and to build the Antora docs (under docs/). Written to be idempotent so it
# can safely run against a cached or partially prepared environment.
set -euo pipefail

LDC_VERSION="ldc-1.42.0"
DLANG_DIR="${HOME}/dlang"
LDC_BIN="${DLANG_DIR}/${LDC_VERSION}/bin"

echo "==> Installing system libraries (build tools + dlangui runtime deps)"
# dlangui renders through OpenGL/SDL2 and loads fonts via FreeType, so the app
# needs the OpenGL, SDL2, FreeType, and X11 development libraries to build/run.
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
ca-certificates curl xz-utils git build-essential pkg-config \
libgl1-mesa-dev libglu1-mesa-dev libsdl2-dev libfreetype6-dev \
libx11-dev libxext-dev libxcursor-dev libxinerama-dev libxi-dev \
libxrandr-dev libxss-dev libdbus-1-dev \
fonts-dejavu-core fonts-liberation

echo "==> Installing D toolchain (${LDC_VERSION}) + dub"
# Matches CI's ldc-latest line (release.yml). LDC ships dub, so no separate dub.
if [ ! -x "${LDC_BIN}/ldc2" ]; then
curl -fsSL https://dlang.org/install.sh -o /tmp/install-d.sh
bash /tmp/install-d.sh "${LDC_VERSION}"
fi
# Put ldc2/ldmd2/dub on the default PATH so no `source activate` is needed.
# LDC resolves its ldc2.conf via the binary's real path, so symlinks are safe.
for tool in ldc2 ldmd2 dub; do
sudo ln -sf "${LDC_BIN}/${tool}" "/usr/local/bin/${tool}"
done

echo "==> Installing Node dependencies (docs + app catalog)"
corepack enable >/dev/null 2>&1 || true
pnpm install --frozen-lockfile
(cd app && pnpm install --frozen-lockfile)

echo "==> Building the DevCentr app (verifies the toolchain end-to-end)"
(cd app && dub build --build=debug)

echo "==> Setup complete: 'cd app && dub build' to build, run ./app/DevCentr under a display."
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ app/build_output.txt
app/*.exe
app/*.pdb
app/*.dll
app/DevCentr
app/dub.selections.json

# Dev Center cache and temporary files
Expand Down
50 changes: 43 additions & 7 deletions app/src/modules/cli_tools/context.d
Original file line number Diff line number Diff line change
@@ -1,17 +1,53 @@
module modules.cli_tools.context;

import modules.cli_tools.model;
import std.process : execute;
import std.process : spawnProcess, tryWait, kill, wait;
import std.stdio : File;
import std.algorithm : canFind;
import core.thread : Thread;
import core.time : msecs, Duration;
import std.datetime.stopwatch : StopWatch, AutoStart;

version (Windows)
private enum nullDevice = "NUL";
else
private enum nullDevice = "/dev/null";

/// Time budget for a single host-context probe. Probes run on the UI thread at
/// startup, so a command that never returns (e.g. `npx <pkg>` fetching an
/// uninstalled package) must not be allowed to block the app indefinitely.
private enum Duration detectTimeout = 4000.msecs;

private bool detectCommandWorks(string detectCmd) {
if (detectCmd.length == 0) return false;
version (Windows) {
auto r = execute(["cmd", "/c", detectCmd ~ " >nul 2>&1"]);
return r.status == 0;
} else {
auto r = execute(["sh", "-c", detectCmd ~ " >/dev/null 2>&1"]);
return r.status == 0;

string[] args;
version (Windows)
args = ["cmd", "/c", detectCmd];
else
args = ["sh", "-c", detectCmd];

try {
// Null stdin keeps interactive prompts (e.g. npx's install confirmation)
// from stalling the probe; null stdout/stderr discards probe output.
auto devNullIn = File(nullDevice, "r");
auto devNullOut = File(nullDevice, "w");
auto pid = spawnProcess(args, devNullIn, devNullOut, devNullOut);

auto sw = StopWatch(AutoStart.yes);
while (true) {
auto res = tryWait(pid);
if (res.terminated)
return res.status == 0;
if (sw.peek() > detectTimeout) {
kill(pid);
wait(pid);
return false;
}
Thread.sleep(50.msecs);
}
} catch (Exception) {
return false;
}
}

Expand Down
6 changes: 6 additions & 0 deletions app/src/modules/toolchain_advisor/ui.d
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,12 @@ class ToolchainAdvisorPanel : VerticalLayout

private void refreshContext()
{
// DecisionStepBox fires its change callback while it is being
// constructed (selectByIndex in its ctor), which reaches here before
// _contextPanel is created. Skip until the panel exists; the explicit
// refreshContext() after construction performs the real initial update.
if (_contextPanel is null)
return;
_contextPanel.update(_focusStepId, _selections);
}
}
4 changes: 2 additions & 2 deletions app/src/modules/workflow_templates_store/browser.d
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ bool openWorkflowTemplatesStore()
else version (Posix)
{
version (OSX)
return spawnProcess("open", [url]).wait() == 0;
return spawnProcess(["open", url]).wait() == 0;
else
return spawnProcess("xdg-open", [url]).wait() == 0;
return spawnProcess(["xdg-open", url]).wait() == 0;
}
else
return false;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "devcentr",
"private": true,
"devDependencies": {
"@antora/lunr-extension": "1.0.0-alpha.13",
"antora": "^3.1.14"
},
"packageManager": "pnpm@9.15.0"
Expand Down
70 changes: 70 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading