Skip to content

notebook #240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
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
19,123 changes: 8,188 additions & 10,935 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@libsql/client": "^0.5.3"
},
"dependencies": {
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-sql": "^6.5.5",
"@dagrejs/dagre": "^1.1.4",
Expand Down Expand Up @@ -94,6 +95,7 @@
"oslo": "^1.1.3",
"react": "^18.2.01",
"react-dom": "^18.2.0",
"react-markdown": "^9.0.3",
"react-resizable-panels": "^1.0.9",
"sonner": "^1.4.41",
"sql-formatter": "^15.3.2",
Expand Down
114 changes: 114 additions & 0 deletions src/components/editor/javascript-editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import CodeMirror, { ReactCodeMirrorRef } from "@uiw/react-codemirror";
import { javascript } from "@codemirror/lang-javascript";
import { forwardRef, useMemo } from "react";
import { tags as t } from "@lezer/highlight";
import createTheme from "@uiw/codemirror-themes";
import { useTheme } from "@/context/theme-provider";
import { indentationMarkers } from "@replit/codemirror-indentation-markers";

interface JsonEditorProps {
value: string;
readOnly?: boolean;
onChange?: (value: string) => void;
}

function useJavascriptTheme() {
const { theme } = useTheme();

return useMemo(() => {
if (theme === "light") {
return createTheme({
theme: "light",
settings: {
background: "#FFFFFF",
foreground: "#000000",
caret: "#FBAC52",
selection: "#FFD420",
selectionMatch: "#FFD420",
gutterBackground: "#fff",
gutterForeground: "#4D4D4C",
gutterBorder: "transparent",
lineHighlight: "#00000012",
fontFamily:
'Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace',
},
styles: [
{
tag: [t.propertyName, t.function(t.variableName)],
color: "#e67e22",
},
{ tag: [t.keyword], color: "#0000FF" },
{ tag: [t.comment, t.blockComment], color: "#95a5a6" },
{ tag: [t.bool, t.null], color: "#696C77" },
{ tag: [t.number], color: "#FF0080" },
{ tag: [t.string], color: "#50A14F" },
],
});
} else {
return createTheme({
theme: "dark",
settings: {
background: "var(--background)",
foreground: "#9cdcfd",
caret: "#c6c6c6",
selection: "#6199ff2f",
selectionMatch: "#72a1ff59",
lineHighlight: "#ffffff0f",
gutterBackground: "var(--background)",
gutterForeground: "#838383",
gutterActiveForeground: "#fff",
fontFamily:
'Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace',
},
styles: [
{
tag: [t.propertyName, t.function(t.variableName)],
color: "#dcdcaa",
},
{ tag: [t.keyword], color: "#c586c0" },
{ tag: [t.comment, t.blockComment], color: "#95a5a6" },
{ tag: [t.bool, t.null], color: "#696C77" },
{ tag: [t.number], color: "#b5cea8" },
{ tag: [t.string], color: "#ce9178" },
{ tag: [t.paren], color: "#9cdcfd" },
{ tag: [t.bracket], color: "#da70d6" },
],
});
}
}, [theme]);
}

const JavascriptEditor = forwardRef<ReactCodeMirrorRef, JsonEditorProps>(
function JavascriptEditor(
{ value, onChange, readOnly }: JsonEditorProps,
ref
) {
const theme = useJavascriptTheme();

return (
<CodeMirror
className="border p-1 rounded"
ref={ref}
autoFocus
readOnly={readOnly}
basicSetup={{
drawSelection: false,
highlightActiveLine: false,
highlightActiveLineGutter: false,
foldGutter: false,
}}
theme={theme}
value={value}
height="100%"
onChange={onChange}
style={{
fontSize: 20,
height: "100%",
}}
extensions={[javascript(), indentationMarkers()]}
/>
);
}
);

export default JavascriptEditor;
12 changes: 8 additions & 4 deletions src/components/gui/database-gui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { normalizedPathname, sendAnalyticEvents } from "@/lib/tracking";
import { useConfig } from "@/context/config-provider";
import { cn } from "@/lib/utils";
import { scc } from "@/core/command";
import { StudioExtensionMenuItem } from "@/core/extension-manager";

export default function DatabaseGui() {
const DEFAULT_WIDTH = 300;
Expand Down Expand Up @@ -129,21 +130,24 @@ export default function DatabaseGui() {
const tabSideMenu = useMemo(() => {
return [
{
text: "New Query",
key: "query",
title: "New Query",
onClick: () => {
scc.tabs.openBuiltinQuery({});
},
},
...extensions.getWindowTabMenu(),
databaseDriver.getFlags().supportCreateUpdateTable
? {
text: "New Table",
key: "table",
title: "New Table",
onClick: () => {
scc.tabs.openBuiltinSchema({ schemaName: currentSchemaName });
},
}
: undefined,
].filter(Boolean) as { text: string; onClick: () => void }[];
}, [currentSchemaName, databaseDriver]);
].filter(Boolean) as StudioExtensionMenuItem[];
}, [currentSchemaName, databaseDriver, extensions]);

// Send to analytic when tab changes.
const previousLogTabKey = useRef<string>("");
Expand Down
3 changes: 1 addition & 2 deletions src/components/gui/schema-sidebar-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,7 @@ export default function SchemaList({ search }: Readonly<SchemaListProps>) {
// Modification Section
...modificationSection,
modificationSection.length > 0 ? { separator: true } : undefined,



{ title: "Refresh", onClick: () => refresh() },
].filter(Boolean) as OpenContextMenuList;
},
Expand Down
1 change: 1 addition & 0 deletions src/components/gui/sql-editor/use-editor-theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default function useCodeEditorTheme({
{ tag: [t.variableName], color: "#006600" },
{ tag: [t.escape], color: "#33CC33" },
{ tag: [t.tagName], color: "#1C02FF" },
{ tag: t.comment, color: "#bdc3c7" },
{ tag: [t.heading], color: "#0C07FF" },
{ tag: [t.quote], color: "#000000" },
{ tag: [t.list], color: "#B90690" },
Expand Down
5 changes: 3 additions & 2 deletions src/components/gui/windows-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import { restrictToHorizontalAxis } from "../lib/dnd-kit";
import { StudioExtensionMenuItem } from "@/core/extension-manager";

export interface WindowTabItemProps {
component: JSX.Element;
Expand All @@ -41,7 +42,7 @@ export interface WindowTabItemProps {
}

interface WindowTabsProps {
menu?: { text: string; onClick: () => void }[];
menu?: StudioExtensionMenuItem[];
tabs: WindowTabItemProps[];
selected: number;
hideCloseButton?: boolean;
Expand Down Expand Up @@ -254,7 +255,7 @@ export default function WindowTabs({
key={menuIdx}
onClick={menuItem.onClick}
>
{menuItem.text}
{menuItem.title}
</DropdownMenuItem>
);
})}
Expand Down
10 changes: 9 additions & 1 deletion src/core/extension-manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export class StudioExtensionContext {
protected beforeQueryHandlers: BeforeQueryHandler[] = [];
protected afterQueryHandlers: AfterQueryHandler[] = [];
protected resourceCreateMenu: StudioExtensionMenuItem[] = [];
protected windowTabMenu: StudioExtensionMenuItem[] = [];
protected resourceContextMenu: Record<string, CreateResourceMenuHandler[]> =
{};

Expand All @@ -82,7 +83,6 @@ export class StudioExtensionContext {
}

registerCreateResourceMenu(menu: StudioExtensionMenuItem) {
console.log("Register", menu);
this.resourceCreateMenu.push(menu);
}

Expand All @@ -96,6 +96,10 @@ export class StudioExtensionContext {
this.resourceContextMenu[group].push(handler);
}
}

registerWindowTabMenu(menu: StudioExtensionMenuItem) {
this.windowTabMenu.push(menu);
}
}
export class StudioExtensionManager extends StudioExtensionContext {
init() {
Expand Down Expand Up @@ -123,6 +127,10 @@ export class StudioExtensionManager extends StudioExtensionContext {
.filter(Boolean) as StudioExtensionMenuItem[];
}

getWindowTabMenu(): Readonly<StudioExtensionMenuItem[]> {
return this.windowTabMenu;
}

async beforeQuery(payload: BeforeQueryPipeline) {
for (const handler of this.beforeQueryHandlers) {
await handler(payload);
Expand Down
4 changes: 4 additions & 0 deletions src/core/extension-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ExtensionMenuItem {
text: string;
onClick: () => void;
}
7 changes: 6 additions & 1 deletion src/core/standard-extension.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
* This contains the standard extensions as a base for all databases.
*/

import NotebookExtension from "@/extensions/notebook";
import QueryHistoryConsoleLogExtension from "@/extensions/query-console-log";
import ViewEditorExtension from "@/extensions/view-editor";

export function createStandardExtensions() {
return [new QueryHistoryConsoleLogExtension(), new ViewEditorExtension()];
return [
new QueryHistoryConsoleLogExtension(),
new ViewEditorExtension(),
new NotebookExtension(),
];
}
29 changes: 29 additions & 0 deletions src/extensions/notebook/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { StudioExtension } from "@/core/extension-base";
import { StudioExtensionManager } from "@/core/extension-manager";
import { createTabExtension } from "@/core/extension-tab";
import { NotebookIcon } from "lucide-react";
import NotebookTab from "./notebook-tab";

const notebookTab = createTabExtension({
name: "notebook",
key: () => "notebook",
generate: () => ({
title: "New Notebook",
component: <NotebookTab />,
icon: NotebookIcon,
}),
});

export default class NotebookExtension extends StudioExtension {
extensionName = "notebook";

init(studio: StudioExtensionManager): void {
studio.registerWindowTabMenu({
key: "notebook",
title: "New Notebook",
onClick: () => {
notebookTab.open({});
},
});
}
}
Loading
Loading