Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e2b9b37
settings for context and page groups
sid597 Aug 8, 2025
da54c72
node settings
sid597 Aug 9, 2025
f83a007
address review
sid597 Aug 10, 2025
ee58296
fix node creation if it does not exist
sid597 Aug 10, 2025
87f38d3
remove unused, refactor
sid597 Aug 10, 2025
5a6bfdd
address coderabbit review
sid597 Aug 10, 2025
9815656
unify spelling, handle fallback
sid597 Aug 11, 2025
37aa7e0
sync
sid597 Aug 17, 2025
4f2807b
address coderabbit code
sid597 Aug 17, 2025
3e430c9
address lint errors
sid597 Aug 19, 2025
0425eee
use async instead of backend
sid597 Aug 19, 2025
5622c64
address review
sid597 Aug 21, 2025
2a177c5
Merge branch 'main' into eng-733-glue-ui-components-to-data-extractio…
sid597 Aug 21, 2025
5ce74d5
Merge branch 'main' into eng-709-settings-panel-for-suggestive-mode
sid597 Aug 21, 2025
590848c
address review
sid597 Aug 21, 2025
e3f7352
bug fixes
sid597 Aug 22, 2025
0f2938f
bug fixes
sid597 Aug 22, 2025
36fc757
Merge branch 'eng-733-glue-ui-components-to-data-extraction-and-datab…
sid597 Aug 22, 2025
4422b0a
unused import
sid597 Aug 22, 2025
ec8109d
Add an input type for platform accounts
maparent Aug 23, 2025
bdb0180
Add an input type for platform accounts
maparent Aug 23, 2025
474dccc
Merge branch 'main' into eng-733-glue-ui-components-to-data-extractio…
sid597 Aug 23, 2025
aa573c8
Merge branch 'eng-764-bulk-account-uploading' of https://github.com/D…
sid597 Aug 23, 2025
c6a806d
Merge branch 'eng-764-bulk-account-uploading' into eng-733-glue-ui-co…
sid597 Aug 23, 2025
18a0d92
Add an input type for platform accounts
maparent Aug 23, 2025
cba3232
Merge branch 'eng-764-bulk-account-uploading' of https://github.com/D…
sid597 Aug 23, 2025
4b7cf9b
Merge branch 'eng-764-bulk-account-uploading' into eng-733-glue-ui-co…
sid597 Aug 23, 2025
a0ea409
bulk upsert accounts, use database function imports
sid597 Aug 24, 2025
6efd37c
Merge branch 'main' into eng-733-glue-ui-components-to-data-extractio…
sid597 Aug 24, 2025
29e8151
add comment for future
sid597 Aug 24, 2025
a11142a
use better import method
sid597 Aug 24, 2025
814149b
Merge branch 'eng-733-glue-ui-components-to-data-extraction-and-datab…
sid597 Aug 24, 2025
92f28d6
commonjs-to-esm
sid597 Aug 24, 2025
d8837f0
Merge branch 'main' into eng-709-settings-panel-for-suggestive-mode
sid597 Aug 24, 2025
3e0ae6f
fix merge errors, fix tree config setting extraction, test functional…
sid597 Aug 24, 2025
0f56967
add progress toaster, use space existence condition for on load sync
sid597 Aug 24, 2025
99c1ae5
defaultvalue
sid597 Aug 24, 2025
d045b21
address review, leaving out update embeddings from here
sid597 Aug 28, 2025
209da92
Merge branch 'main' into eng-709-settings-panel-for-suggestive-mode
sid597 Aug 28, 2025
6406595
Merge branch 'main' into eng-709-settings-panel-for-suggestive-mode
sid597 Aug 29, 2025
3e142de
update embeddings
sid597 Aug 29, 2025
a0e191a
Merge branch 'main' into eng-709-settings-panel-for-suggestive-mode
sid597 Aug 31, 2025
569df7a
ENG-818: Declare dbDotEnv as mjs explicitly
maparent Sep 1, 2025
d6ff391
Merge branch 'main' into eng-709-settings-panel-for-suggestive-mode
sid597 Sep 1, 2025
f65a5c1
Merge branch 'eng-818-make-dbdotenv-explicitly-a-module' into eng-709…
sid597 Sep 1, 2025
58d119b
Merge branch 'main' into eng-709-settings-panel-for-suggestive-mode
sid597 Sep 8, 2025
b91efed
reverting create a new pr for update embeddings
sid597 Sep 8, 2025
09b6629
Merge branch 'main' into eng-709-settings-panel-for-suggestive-mode
sid597 Sep 8, 2025
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
125 changes: 125 additions & 0 deletions apps/roam/src/components/settings/DiscourseNodeSuggestiveRules.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React, {
useState,
useMemo,
useEffect,
useRef,
useCallback,
} from "react";
import { Button, Intent } from "@blueprintjs/core";
import BlocksPanel from "roamjs-components/components/ConfigPanels/BlocksPanel";
import FlagPanel from "roamjs-components/components/ConfigPanels/FlagPanel";
import TextPanel from "roamjs-components/components/ConfigPanels/TextPanel";
import getSubTree from "roamjs-components/util/getSubTree";
import { DiscourseNode } from "~/utils/getDiscourseNodes";
import extractRef from "roamjs-components/util/extractRef";

const BlockRenderer = ({ uid }: { uid: string }) => {
const containerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const container = containerRef.current;
if (container) {
container.innerHTML = "";

window.roamAlphaAPI.ui.components.renderBlock({
uid: uid,
el: container,
});
}
}, [uid]);

return <div ref={containerRef} className="my-2 rounded border p-2" />;
};

const DiscourseNodeSuggestiveRules = ({
node,
parentUid,
}: {
node: DiscourseNode;
parentUid: string;
}) => {
const nodeUid = node.type;

const [embeddingRef, setEmbeddingRef] = useState(node.embeddingRef);

useEffect(() => {
setEmbeddingRef(node.embeddingRef || "");
}, [node.embeddingRef]);

const blockUidToRender = useMemo(
() => extractRef(embeddingRef),
[embeddingRef],
);

const templateUid = useMemo(
() =>
getSubTree({
parentUid: nodeUid,
key: "Template",
}).uid || "",
[nodeUid],
);

const handleEmbeddingRefChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
setEmbeddingRef(newValue);
},
[],
);

return (
<div className="flex flex-col gap-4 p-4">
<BlocksPanel
title="Template"
description={`The template that auto fills ${node.text} page when generated.`}
order={0}
parentUid={nodeUid}
uid={templateUid}
defaultValue={node.template}
/>

<TextPanel
title="Embedding Block Ref"
description="Copy block ref from template which you want to be embedded and ranked."
order={0}
uid={node.embeddingRefUid || ""}
parentUid={parentUid}
value={node.embeddingRef || ""}
defaultValue={node.embeddingRef || ""}
options={{
placeholder: "((block-uid))",
onChange: handleEmbeddingRefChange,
}}
/>

{blockUidToRender && (
<div>
<div className="mb-1 text-sm text-gray-600">Preview:</div>
<BlockRenderer uid={blockUidToRender} />
</div>
)}

<FlagPanel
title="First Child"
description="If the block is the first child of the embedding block ref, it will be embedded and ranked."
order={1}
uid={node.isFirstChild?.uid || ""}
parentUid={parentUid}
value={node.isFirstChild?.value || false}
/>

{/* TODO: Add a button to update embeddings in seperate PR */}
<Button
text="Update Embeddings"
intent={Intent.NONE}
onClick={() => console.log("Not implemented")}
className="w-52"
disabled
/>
</div>
);
};

export default DiscourseNodeSuggestiveRules;
15 changes: 15 additions & 0 deletions apps/roam/src/components/settings/NodeConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React, { useState, useCallback, useRef, useEffect } from "react";
import { DiscourseNode } from "~/utils/getDiscourseNodes";
import FlagPanel from "roamjs-components/components/ConfigPanels/FlagPanel";
Expand All @@ -22,6 +23,7 @@ import { OnloadArgs } from "roamjs-components/types";
import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid";
import createBlock from "roamjs-components/writes/createBlock";
import updateBlock from "roamjs-components/writes/updateBlock";
import DiscourseNodeSuggestiveRules from "./DiscourseNodeSuggestiveRules";

const ValidatedInputPanel = ({
label,
Expand Down Expand Up @@ -161,6 +163,7 @@ const NodeConfig = ({
const graphOverviewUid = getUid("Graph Overview");
const specificationUid = getUid("Specification");
const indexUid = getUid("Index");
const suggestiveRulesUid = getUid("Suggestive Rules");
const attributeNode = getSubTree({
parentUid: node.type,
key: "Attributes",
Expand Down Expand Up @@ -383,6 +386,18 @@ const NodeConfig = ({
</div>
}
/>
<Tab
id="suggestive-mode"
title="Suggestive Mode"
panel={
<div className="flex flex-col gap-4 p-1">
<DiscourseNodeSuggestiveRules
node={node}
parentUid={suggestiveRulesUid}
/>
</div>
}
/>
</Tabs>
</>
);
Expand Down
206 changes: 206 additions & 0 deletions apps/roam/src/components/settings/PageGroupPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React, { useState, useCallback } from "react";
import { Label, Button, Intent, Tag } from "@blueprintjs/core";
import Description from "roamjs-components/components/Description";
import AutocompleteInput from "roamjs-components/components/AutocompleteInput";
import createBlock from "roamjs-components/writes/createBlock";
import deleteBlock from "roamjs-components/writes/deleteBlock";
import getAllPageNames from "roamjs-components/queries/getAllPageNames";
import { type PageGroup } from "~/utils/getSuggestiveModeConfigSettings";

const PageGroupsPanel = ({
uid,
initialGroups,
}: {
uid: string;
initialGroups: PageGroup[];
}) => {
const [pageGroups, setPageGroups] = useState<PageGroup[]>(initialGroups);

const [newGroupName, setNewGroupName] = useState("");
const [newPageInputs, setNewPageInputs] = useState<Record<string, string>>(
{},
);
const [autocompleteKeys, setAutocompleteKeys] = useState<
Record<string, number>
>({});

const addGroup = async (name: string) => {
if (!name || pageGroups.some((g) => g.name === name)) return;
try {
const newGroupUid = await createBlock({
parentUid: uid,
node: { text: name },
});
setPageGroups([...pageGroups, { uid: newGroupUid, name, pages: [] }]);
setNewGroupName("");
} catch (e) {
console.error("Error adding group", e);
}
};

const removeGroup = async (groupUid: string) => {
try {
await deleteBlock(groupUid);
setPageGroups(pageGroups.filter((g) => g.uid !== groupUid));
} catch (e) {
console.error("Error removing group", e);
}
};

const addPageToGroup = async (groupUid: string, page: string) => {
const group = pageGroups.find((g) => g.uid === groupUid);
if (!page || group?.pages.some((p) => p.name === page)) {
return;
}
try {
const newPageUid = await createBlock({
parentUid: groupUid,
node: { text: page },
});
setPageGroups(
pageGroups.map((g) =>
g.uid === groupUid
? { ...g, pages: [...g.pages, { uid: newPageUid, name: page }] }
: g,
),
);
setNewPageInputs((prev) => ({
...prev,
[groupUid]: "",
}));
setAutocompleteKeys((prev) => ({
...prev,
[groupUid]: (prev[groupUid] || 0) + 1,
}));
} catch (e) {
console.error("Error adding page to group", e);
}
};

const removePageFromGroup = async (groupUid: string, pageUid: string) => {
try {
await deleteBlock(pageUid);
setPageGroups(
pageGroups.map((g) =>
g.uid === groupUid
? { ...g, pages: g.pages.filter((p) => p.uid !== pageUid) }
: g,
),
);
} catch (e) {
console.error("Error removing page from group", e);
}
};

const getPageInput = (groupUid: string) => newPageInputs[groupUid] || "";
const setPageInput = useCallback((groupUid: string, value: string) => {
setTimeout(() => {
setNewPageInputs((prev) => ({
...prev,
[groupUid]: value,
}));
}, 0);
}, []);
const getAutocompleteKey = (groupUid: string) =>
autocompleteKeys[groupUid] || 0;

return (
<Label>
Page Groups
<Description
description={
"Organize pages into named groups that will be can be selected when generating Discourse Suggestions."
}
/>
<div className="flex flex-col gap-2">
{/* Add Group */}
<div className="flex items-center gap-2">
<AutocompleteInput
value={newGroupName}
setValue={setNewGroupName}
placeholder="Page group name…"
options={[]}
/>
<Button
icon="plus"
small
minimal
disabled={
!newGroupName || pageGroups.some((g) => g.name === newGroupName)
}
onClick={() => void addGroup(newGroupName)}
/>
</div>

{/* Existing Groups */}
{Object.keys(pageGroups).length === 0 && (
<div className="text-sm italic text-gray-500">No groups added.</div>
)}
{pageGroups.map((group) => (
<div key={group.uid} className="rounded border p-2">
<div className="mb-1 flex items-center justify-between">
<span className="font-semibold">{group.name}</span>
<Button
icon="trash"
minimal
small
intent={Intent.DANGER}
onClick={() => void removeGroup(group.uid)}
/>
</div>
<div className="flex flex-wrap items-center gap-2">
<div
className="flex-0 min-w-[160px]"
onKeyDown={(e) => {
if (e.key === "Enter" && getPageInput(group.uid)) {
e.preventDefault();
e.stopPropagation();
void addPageToGroup(group.uid, getPageInput(group.uid));
}
}}
>
<AutocompleteInput
key={getAutocompleteKey(group.uid)}
value={getPageInput(group.uid)}
placeholder="Add page…"
setValue={(v) => setPageInput(group.uid, v)}
options={getAllPageNames()}
maxItemsDisplayed={50}
/>
</div>
<Button
icon="plus"
small
minimal
onClick={() =>
void addPageToGroup(group.uid, getPageInput(group.uid))
}
disabled={
!getPageInput(group.uid) ||
group.pages.some((p) => p.name === getPageInput(group.uid))
}
/>
</div>
{group.pages.length > 0 && (
<div className="mt-2 flex flex-wrap gap-1">
{group.pages.map((p) => (
<Tag
key={p.uid}
onRemove={() => void removePageFromGroup(group.uid, p.uid)}
round
minimal
>
{p.name}
</Tag>
))}
</div>
)}
</div>
))}
</div>
</Label>
);
};

export default PageGroupsPanel;
7 changes: 7 additions & 0 deletions apps/roam/src/components/settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import sendErrorEmail from "~/utils/sendErrorEmail";
import HomePersonalSettings from "./HomePersonalSettings";
import refreshConfigTree from "~/utils/refreshConfigTree";
import { FeedbackWidget } from "~/components/BirdEatsBugs";
import SuggestiveModeSettings from "./SuggestiveModeSettings";
import { getVersionWithDate } from "~/utils/getVersion";

type SectionHeaderProps = {
Expand Down Expand Up @@ -161,6 +162,12 @@ export const SettingsDialog = ({
className="overflow-y-auto"
panel={<DiscourseGraphExport />}
/>
<Tab
id="suggestive-mode-settings"
title="Suggestive Mode"
className="overflow-y-auto"
panel={<SuggestiveModeSettings />}
/>
<SectionHeader>Grammar</SectionHeader>
<Tab
id="discourse-relations"
Expand Down
Loading