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
37 changes: 23 additions & 14 deletions pnpm-lock.yaml

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

5 changes: 5 additions & 0 deletions prototypes/loaded-dialog/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## 0.0.0 - 2026-08-17

- Created the Loaded Dialog prototype scaffold.
20 changes: 20 additions & 0 deletions prototypes/loaded-dialog/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Loaded Dialog

Shows a dialog confirming that the extension has loaded.

## Status

Internal prototype for evaluation by Discourse Graphs.

## Features

- Opens a Blueprint alert when the extension loads.
- Confirms that the extension loaded successfully with a single **Got it** action.

## Install

Load this developer-extension URL in Roam:

```text
https://discoursegraphs.com/releases/prototypes/loaded-dialog/
```
21 changes: 21 additions & 0 deletions prototypes/loaded-dialog/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "loaded-dialog",
"version": "0.0.0",
"private": true,
"description": "Shows a dialog confirming that the extension has loaded.",
"type": "module",
"scripts": {
"start": "roam-prototype dev",
"build": "roam-prototype build",
"test": "vitest run --passWithNoTests"
},
"dependencies": {
"roamjs-components": "catalog:",
"use-sync-external-store": "catalog:"
},
"devDependencies": {
"@discoursegraphs/extension-base": "workspace:*",
"jsdom": "catalog:",
"vitest": "catalog:"
}
}
10 changes: 10 additions & 0 deletions prototypes/loaded-dialog/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { render as renderAlert } from "roamjs-components/components/SimpleAlert";
import { runExtension } from "roamjs-components/util";
import "./styles.css";

export default runExtension(async () => {
await renderAlert({
content: "Loaded Dialog has loaded successfully.",
confirmText: "Got it",
});
});
Comment on lines +5 to +10

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Dialog can stay on screen after the extension is turned off

The confirmation dialog is opened without keeping any handle to close it (renderAlert(...) at prototypes/loaded-dialog/src/index.ts:6-9) and no cleanup step is returned, so a dialog still on screen when the extension is disabled or reloaded stays there forever.
Impact: A user who disables or reloads the extension while the dialog is open is left with a stuck dialog, and reloading repeatedly can stack leftover dialogs.

Overlay mount is never registered with the runExtension lifecycle

roamjs-components/components/SimpleAlert's render is built with createOverlayRender, which appends its own container to the document and returns an unmount function. The prototype discards that return value and, unlike the scaffold template (packages/extension-base/template/src/index.ts:17-21), does not return an unload handler from the runExtension callback, so nothing removes the container or unmounts the React tree at unload time. AGENTS.md requires "Dispose observers, listeners, commands, timers, and mounted UI when the extension unloads", and packages/extension-base/skills/react-rendering/SKILL.md:23 requires owned React roots/containers to be registered for cleanup. The PR description's claim that runExtension owns the mounted alert does not hold because the overlay is mounted outside the registry.

Suggested change
export default runExtension(async () => {
await renderAlert({
content: "Loaded Dialog has loaded successfully.",
confirmText: "Got it",
});
});
export default runExtension(async () => {
const unmountAlert = renderAlert({
content: "Loaded Dialog has loaded successfully.",
confirmText: "Got it",
});
return {
unload: () => {
unmountAlert?.();
},
};
});
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

3 changes: 3 additions & 0 deletions prototypes/loaded-dialog/src/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.dg-prototype-loaded-dialog {
--dg-prototype-name: "loaded-dialog";
}
6 changes: 6 additions & 0 deletions prototypes/loaded-dialog/tailwind.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const base = require("../../packages/extension-base/tailwind.config.cjs");

module.exports = {
...base,
content: ["./src/**/*.{js,jsx,ts,tsx}"],
};
16 changes: 16 additions & 0 deletions prototypes/loaded-dialog/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "../../packages/extension-base/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": [
"./src/*"
]
}
},
"include": [
"src",
"tests",
"vitest.config.ts"
]
}
8 changes: 8 additions & 0 deletions prototypes/loaded-dialog/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from "vitest/config";

export default defineConfig({
test: {
environment: "jsdom",
restoreMocks: true,
},
});