Skip to content

Commit

Permalink
feat: TTD dialog tweaks (excalidraw#7346)
Browse files Browse the repository at this point in the history
* tweaks to TTD dialog ~ prepping for settings dialog

* tweaks to ttd parsing & error logging
  • Loading branch information
dwelle authored Nov 27, 2023
1 parent fe75f29 commit dd220bc
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 104 deletions.
3 changes: 2 additions & 1 deletion src/components/Actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,9 @@ export const ShapesSwitcher = ({
onSelect={() => {
trackEvent("ai", "open-settings", "d2c");
app.setOpenDialog({
name: "magicSettings",
name: "settings",
source: "settings",
tab: "diagram-to-code",
});
}}
icon={OpenAIIcon}
Expand Down
12 changes: 10 additions & 2 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1700,7 +1700,11 @@ class App extends React.Component<AppProps, AppState> {
) {
if (!this.OPENAI_KEY) {
this.setState({
openDialog: { name: "magicSettings", source: "generation" },
openDialog: {
name: "settings",
tab: "diagram-to-code",
source: "generation",
},
});
trackEvent("ai", "generate (missing key)", "d2c");
return;
Expand Down Expand Up @@ -1871,7 +1875,11 @@ class App extends React.Component<AppProps, AppState> {
public onMagicframeToolSelect = () => {
if (!this.OPENAI_KEY) {
this.setState({
openDialog: { name: "magicSettings", source: "tool" },
openDialog: {
name: "settings",
tab: "diagram-to-code",
source: "tool",
},
});
trackEvent("ai", "tool-select (missing key)", "d2c");
return;
Expand Down
4 changes: 2 additions & 2 deletions src/components/LayerUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -461,14 +461,14 @@ const LayerUI = ({
}}
/>
)}
{appState.openDialog?.name === "magicSettings" && (
{appState.openDialog?.name === "settings" && (
<MagicSettings
openAIKey={openAIKey}
isPersisted={isOpenAIKeyPersisted}
onChange={onOpenAIAPIKeyChange}
onConfirm={(apiKey, shouldPersist) => {
const source =
appState.openDialog?.name === "magicSettings"
appState.openDialog?.name === "settings"
? appState.openDialog?.source
: "settings";
setAppState({ openDialog: null }, () => {
Expand Down
9 changes: 9 additions & 0 deletions src/components/MagicSettings.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
.excalidraw {
.MagicSettings {
.Island {
height: 100%;
display: flex;
flex-direction: column;
}
}

.MagicSettings-confirm {
padding: 0.5rem 1rem;
}

.MagicSettings__confirm {
margin-top: 2rem;
margin-right: auto;
}
}
160 changes: 95 additions & 65 deletions src/components/MagicSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { InlineIcon } from "./InlineIcon";
import { Paragraph } from "./Paragraph";

import "./MagicSettings.scss";
import TTDDialogTabs from "./TTDDialog/TTDDialogTabs";
import { TTDDialogTab } from "./TTDDialog/TTDDialogTab";

export const MagicSettings = (props: {
openAIKey: string | null;
Expand All @@ -18,16 +20,21 @@ export const MagicSettings = (props: {
onConfirm: (key: string, shouldPersist: boolean) => void;
onClose: () => void;
}) => {
const { theme } = useUIAppState();
const [keyInputValue, setKeyInputValue] = useState(props.openAIKey || "");
const [shouldPersist, setShouldPersist] = useState<boolean>(
props.isPersisted,
);

const appState = useUIAppState();

const onConfirm = () => {
props.onConfirm(keyInputValue.trim(), shouldPersist);
};

if (appState.openDialog?.name !== "settings") {
return null;
}

return (
<Dialog
onCloseRequest={() => {
Expand All @@ -36,7 +43,7 @@ export const MagicSettings = (props: {
}}
title={
<div style={{ display: "flex" }}>
Diagram to Code (AI){" "}
Wireframe to Code (AI){" "}
<div
style={{
display: "flex",
Expand All @@ -46,7 +53,8 @@ export const MagicSettings = (props: {
marginLeft: "1rem",
fontSize: 14,
borderRadius: "12px",
background: theme === "light" ? "#FFCCCC" : "#703333",
color: "#000",
background: "pink",
}}
>
Experimental
Expand All @@ -56,75 +64,97 @@ export const MagicSettings = (props: {
className="MagicSettings"
autofocus={false}
>
<Paragraph
{/* <h2
style={{
display: "inline-flex",
alignItems: "center",
marginBottom: 0,
margin: 0,
fontSize: "1.25rem",
paddingLeft: "2.5rem",
}}
>
For the diagram-to-code feature we use <InlineIcon icon={OpenAIIcon} />
OpenAI.
</Paragraph>
<Paragraph>
While the OpenAI API is in beta, its use is strictly limited — as such
we require you use your own API key. You can create an{" "}
<a
href="https://platform.openai.com/login?launch"
rel="noopener noreferrer"
target="_blank"
>
OpenAI account
</a>
, add a small credit (5 USD minimum), and{" "}
<a
href="https://platform.openai.com/api-keys"
rel="noopener noreferrer"
target="_blank"
AI Settings
</h2> */}
<TTDDialogTabs dialog="settings" tab={appState.openDialog.tab}>
{/* <TTDDialogTabTriggers>
<TTDDialogTabTrigger tab="text-to-diagram">
<InlineIcon icon={brainIcon} /> Text to diagram
</TTDDialogTabTrigger>
<TTDDialogTabTrigger tab="diagram-to-code">
<InlineIcon icon={MagicIcon} /> Wireframe to code
</TTDDialogTabTrigger>
</TTDDialogTabTriggers> */}
{/* <TTDDialogTab className="ttd-dialog-content" tab="text-to-diagram">
TODO
</TTDDialogTab> */}
<TTDDialogTab
// className="ttd-dialog-content"
tab="diagram-to-code"
>
generate your own API key
</a>
.
</Paragraph>
<Paragraph>
Your OpenAI key does not leave the browser, and you can also set your
own limit in your OpenAI account dashboard if needed.
</Paragraph>
<TextField
isRedacted
value={keyInputValue}
placeholder="Paste your API key here"
label="OpenAI API key"
onChange={(value) => {
setKeyInputValue(value);
props.onChange(value.trim(), shouldPersist);
}}
selectOnRender
onKeyDown={(event) => event.key === KEYS.ENTER && onConfirm()}
/>
<Paragraph>
By default, your API token is not persisted anywhere so you'll need to
insert it again after reload. But, you can persist locally in your
browser below.
</Paragraph>
<Paragraph>
For the diagram-to-code feature we use{" "}
<InlineIcon icon={OpenAIIcon} />
OpenAI.
</Paragraph>
<Paragraph>
While the OpenAI API is in beta, its use is strictly limited — as
such we require you use your own API key. You can create an{" "}
<a
href="https://platform.openai.com/login?launch"
rel="noopener noreferrer"
target="_blank"
>
OpenAI account
</a>
, add a small credit (5 USD minimum), and{" "}
<a
href="https://platform.openai.com/api-keys"
rel="noopener noreferrer"
target="_blank"
>
generate your own API key
</a>
.
</Paragraph>
<Paragraph>
Your OpenAI key does not leave the browser, and you can also set
your own limit in your OpenAI account dashboard if needed.
</Paragraph>
<TextField
isRedacted
value={keyInputValue}
placeholder="Paste your API key here"
label="OpenAI API key"
onChange={(value) => {
setKeyInputValue(value);
props.onChange(value.trim(), shouldPersist);
}}
selectOnRender
onKeyDown={(event) => event.key === KEYS.ENTER && onConfirm()}
/>
<Paragraph>
By default, your API token is not persisted anywhere so you'll need
to insert it again after reload. But, you can persist locally in
your browser below.
</Paragraph>

<CheckboxItem checked={shouldPersist} onChange={setShouldPersist}>
Persist API key in browser storage
</CheckboxItem>
<CheckboxItem checked={shouldPersist} onChange={setShouldPersist}>
Persist API key in browser storage
</CheckboxItem>

<Paragraph>
Once API key is set, you can use the <InlineIcon icon={MagicIcon} />{" "}
tool to wrap your elements in a frame that will then allow you to turn
it into code. This dialog can be accessed using the <b>AI Settings</b>{" "}
<InlineIcon icon={OpenAIIcon} />.
</Paragraph>
<Paragraph>
Once API key is set, you can use the <InlineIcon icon={MagicIcon} />{" "}
tool to wrap your elements in a frame that will then allow you to
turn it into code. This dialog can be accessed using the{" "}
<b>AI Settings</b> <InlineIcon icon={OpenAIIcon} />.
</Paragraph>

<FilledButton
className="MagicSettings__confirm"
size="large"
label="Confirm"
onClick={onConfirm}
/>
<FilledButton
className="MagicSettings__confirm"
size="large"
label="Confirm"
onClick={onConfirm}
/>
</TTDDialogTab>
</TTDDialogTabs>
</Dialog>
);
};
5 changes: 4 additions & 1 deletion src/components/Modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@
overflow: auto;
padding: calc(var(--space-factor) * 10);

display: flex;
flex-direction: column;

.Island {
padding: 2.5rem !important;
padding: 2.5rem;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/TTDDialog/MermaidToExcalidraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const MermaidToExcalidraw = ({
data,
mermaidToExcalidrawLib,
setError,
text: deferredText,
mermaidDefinition: deferredText,
}).catch(() => {});
}, [deferredText, mermaidToExcalidrawLib]);

Expand Down
35 changes: 30 additions & 5 deletions src/components/TTDDialog/TTDDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const TTDDialogBase = withInternalFallback(
tab,
...rest
}: {
tab: string;
tab: "text-to-diagram" | "mermaid";
} & (
| {
onTextSubmit(value: string): Promise<OnTestSubmitRetValue>;
Expand Down Expand Up @@ -150,11 +150,19 @@ export const TTDDialogBase = withInternalFallback(
data,
mermaidToExcalidrawLib,
setError,
text: generatedResponse,
mermaidDefinition: generatedResponse,
});
trackEvent("ai", "mermaid parse success", "ttd");
saveMermaidDataToStorage(generatedResponse);
} catch (error: any) {
console.info(
`%cTTD mermaid render errror: ${error.message}`,
"color: red",
);
console.info(
`>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nTTD mermaid definition render errror: ${error.message}`,
"color: yellow",
);
trackEvent("ai", "mermaid parse failed", "ttd");
setError(
new Error(
Expand Down Expand Up @@ -206,17 +214,34 @@ export const TTDDialogBase = withInternalFallback(
app.setOpenDialog(null);
}}
size={1200}
title=""
title={false}
{...rest}
autofocus={false}
>
<TTDDialogTabs tab={tab}>
<TTDDialogTabs dialog="ttd" tab={tab}>
{"__fallback" in rest && rest.__fallback ? (
<p className="dialog-mermaid-title">{t("mermaid.title")}</p>
) : (
<TTDDialogTabTriggers>
<TTDDialogTabTrigger tab="text-to-diagram">
{t("labels.textToDiagram")}
<div style={{ display: "flex", alignItems: "center" }}>
{t("labels.textToDiagram")}
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
padding: "1px 6px",
marginLeft: "10px",
fontSize: 10,
borderRadius: "12px",
background: "pink",
color: "#000",
}}
>
AI Beta
</div>
</div>
</TTDDialogTabTrigger>
<TTDDialogTabTrigger tab="mermaid">Mermaid</TTDDialogTabTrigger>
</TTDDialogTabTriggers>
Expand Down
Loading

0 comments on commit dd220bc

Please sign in to comment.