chore(sql_console): integrate table reference

This commit is contained in:
Elian Doran 2026-01-17 22:38:18 +02:00
parent 476247beb5
commit 4c4e5b85e9
No known key found for this signature in database
6 changed files with 109 additions and 103 deletions

View File

@ -46,7 +46,6 @@ import ScrollPadding from "../widgets/scroll_padding.js";
import SearchResult from "../widgets/search_result.jsx"; import SearchResult from "../widgets/search_result.jsx";
import SharedInfo from "../widgets/shared_info.jsx"; import SharedInfo from "../widgets/shared_info.jsx";
import RightPanelContainer from "../widgets/sidebar/RightPanelContainer.jsx"; import RightPanelContainer from "../widgets/sidebar/RightPanelContainer.jsx";
import SqlTableSchemas from "../widgets/sql_table_schemas.js";
import TabRowWidget from "../widgets/tab_row.js"; import TabRowWidget from "../widgets/tab_row.js";
import TabHistoryNavigationButtons from "../widgets/TabHistoryNavigationButtons.jsx"; import TabHistoryNavigationButtons from "../widgets/TabHistoryNavigationButtons.jsx";
import TitleBarButtons from "../widgets/title_bar_buttons.jsx"; import TitleBarButtons from "../widgets/title_bar_buttons.jsx";
@ -162,7 +161,6 @@ export default class DesktopLayout {
.child(<SharedInfo />) .child(<SharedInfo />)
) )
.optChild(!isNewLayout, <PromotedAttributes />) .optChild(!isNewLayout, <PromotedAttributes />)
.child(<SqlTableSchemas />)
.child(<NoteDetail />) .child(<NoteDetail />)
.child(<NoteList media="screen" />) .child(<NoteList media="screen" />)
.child(<SearchResult />) .child(<SearchResult />)

View File

@ -1,34 +0,0 @@
.sql-table-schemas-widget {
padding: 12px;
padding-inline-end: 10%;
contain: none !important;
}
.sql-table-schemas > .dropdown {
display: inline-block !important;
}
.sql-table-schemas button.btn {
padding: 0.25rem 0.4rem;
font-size: 0.875rem;
line-height: 0.5;
border: 1px solid var(--button-border-color);
border-radius: var(--button-border-radius);
background: var(--button-background-color);
color: var(--button-text-color);
cursor: pointer;
}
.table-schema td {
padding: 5px;
}
.dropdown .table-schema {
font-family: var(--monospace-font-family);
font-size: .85em;
}
/* Data type */
.dropdown .table-schema td:nth-child(2) {
color: var(--muted-text-color);
}

View File

@ -1,46 +0,0 @@
import { useEffect, useState } from "preact/hooks";
import { t } from "../services/i18n";
import { useNoteContext } from "./react/hooks";
import "./sql_table_schemas.css";
import { SchemaResponse } from "@triliumnext/commons";
import server from "../services/server";
import Dropdown from "./react/Dropdown";
export default function SqlTableSchemas() {
const { note } = useNoteContext();
const [ schemas, setSchemas ] = useState<SchemaResponse[]>();
useEffect(() => {
server.get<SchemaResponse[]>("sql/schema").then(setSchemas);
}, []);
const isEnabled = note?.mime === "text/x-sqlite;schema=trilium" && schemas;
return (
<div className={`sql-table-schemas-widget ${!isEnabled ? "hidden-ext" : ""}`}>
{isEnabled && (
<>
{t("sql_table_schemas.tables")}{": "}
<span class="sql-table-schemas">
{schemas.map(({ name, columns }) => (
<>
<Dropdown text={name} noSelectButtonStyle hideToggleArrow
>
<table className="table-schema">
{columns.map(column => (
<tr>
<td>{column.name}</td>
<td>{column.type}</td>
</tr>
))}
</table>
</Dropdown>
{" "}
</>
))}
</span>
</>
)}
</div>
)
}

View File

@ -32,5 +32,46 @@
} }
} }
} }
.sql-table-schemas-widget {
padding: 12px;
padding-inline-end: 10%;
contain: none !important;
.sql-table-schemas {
display: flex;
flex-wrap: wrap;
gap: 0.25em;
}
> .dropdown {
display: inline-block !important;
}
button.btn {
padding: 0.25rem 0.4rem;
font-size: 0.875rem;
line-height: 0.5;
border: 1px solid var(--button-border-color);
border-radius: var(--button-border-radius);
background: var(--button-background-color);
color: var(--button-text-color);
cursor: pointer;
}
.table-schema td {
padding: 5px;
}
.dropdown .table-schema {
font-family: var(--monospace-font-family);
font-size: .85em;
}
/* Data type */
.dropdown .table-schema td:nth-child(2) {
color: var(--muted-text-color);
}
}
} }

View File

@ -1,26 +1,31 @@
import "./SqlConsole.css"; import "./SqlConsole.css";
import { SqlExecuteResults } from "@triliumnext/commons"; import { SchemaResponse, SqlExecuteResults } from "@triliumnext/commons";
import { useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { ClipboardModule, EditModule, ExportModule, FilterModule, FormatModule, FrozenColumnsModule, KeybindingsModule, ResizeColumnsModule, SelectRangeModule, SelectRowModule, SortModule } from "tabulator-tables"; import { ClipboardModule, EditModule, ExportModule, FilterModule, FormatModule, FrozenColumnsModule, KeybindingsModule, ResizeColumnsModule, SelectRangeModule, SelectRowModule, SortModule } from "tabulator-tables";
import { t } from "../../services/i18n"; import { t } from "../../services/i18n";
import server from "../../services/server";
import Tabulator from "../collections/table/tabulator"; import Tabulator from "../collections/table/tabulator";
import Alert from "../react/Alert"; import Alert from "../react/Alert";
import Dropdown from "../react/Dropdown";
import { useTriliumEvent } from "../react/hooks"; import { useTriliumEvent } from "../react/hooks";
import SplitEditor from "./helpers/SplitEditor"; import SplitEditor from "./helpers/SplitEditor";
import { TypeWidgetProps } from "./type_widget"; import { TypeWidgetProps } from "./type_widget";
export default function SqlConsole(props: TypeWidgetProps) { export default function SqlConsole(props: TypeWidgetProps) {
return ( return (
<SplitEditor <>
noteType="code" <SplitEditor
{...props} noteType="code"
previewContent={<SqlResults {...props} />} {...props}
splitOptions={{ editorBefore={<SqlTableSchemas {...props} />}
sizes: [ 90, 10 ] previewContent={<SqlResults {...props} />}
}} splitOptions={{
/> sizes: [ 70, 30 ]
}}
/>
</>
); );
} }
@ -94,3 +99,41 @@ function SqlResultTable({ rows }: { rows: object[] }) {
/> />
); );
} }
export function SqlTableSchemas({ note }: TypeWidgetProps) {
const [ schemas, setSchemas ] = useState<SchemaResponse[]>();
useEffect(() => {
server.get<SchemaResponse[]>("sql/schema").then(setSchemas);
}, []);
const isEnabled = note?.mime === "text/x-sqlite;schema=trilium" && schemas;
return (
<div className={`sql-table-schemas-widget ${!isEnabled ? "hidden-ext" : ""}`}>
{isEnabled && (
<>
{t("sql_table_schemas.tables")}{": "}
<span class="sql-table-schemas">
{schemas.map(({ name, columns }) => (
<>
<Dropdown text={name} noSelectButtonStyle hideToggleArrow
>
<table className="table-schema">
{columns.map(column => (
<tr>
<td>{column.name}</td>
<td>{column.type}</td>
</tr>
))}
</table>
</Dropdown>
{" "}
</>
))}
</span>
</>
)}
</div>
);
}

View File

@ -1,13 +1,15 @@
import "./SplitEditor.css";
import Split from "@triliumnext/split.js";
import { ComponentChildren } from "preact";
import { useEffect, useRef } from "preact/hooks"; import { useEffect, useRef } from "preact/hooks";
import { DEFAULT_GUTTER_SIZE } from "../../../services/resizer";
import utils, { isMobile } from "../../../services/utils"; import utils, { isMobile } from "../../../services/utils";
import ActionButton, { ActionButtonProps } from "../../react/ActionButton";
import Admonition from "../../react/Admonition"; import Admonition from "../../react/Admonition";
import { useNoteLabelBoolean, useTriliumOption } from "../../react/hooks"; import { useNoteLabelBoolean, useTriliumOption } from "../../react/hooks";
import "./SplitEditor.css";
import Split from "@triliumnext/split.js";
import { DEFAULT_GUTTER_SIZE } from "../../../services/resizer";
import { EditableCode, EditableCodeProps } from "../code/Code"; import { EditableCode, EditableCodeProps } from "../code/Code";
import { ComponentChildren } from "preact";
import ActionButton, { ActionButtonProps } from "../../react/ActionButton";
export interface SplitEditorProps extends EditableCodeProps { export interface SplitEditorProps extends EditableCodeProps {
className?: string; className?: string;
@ -15,6 +17,7 @@ export interface SplitEditorProps extends EditableCodeProps {
splitOptions?: Split.Options; splitOptions?: Split.Options;
previewContent: ComponentChildren; previewContent: ComponentChildren;
previewButtons?: ComponentChildren; previewButtons?: ComponentChildren;
editorBefore?: ComponentChildren;
} }
/** /**
@ -26,13 +29,14 @@ export interface SplitEditorProps extends EditableCodeProps {
* - Can display errors to the user via {@link setError}. * - Can display errors to the user via {@link setError}.
* - Horizontal or vertical orientation for the editor/preview split, adjustable via the switch split orientation button floating button. * - Horizontal or vertical orientation for the editor/preview split, adjustable via the switch split orientation button floating button.
*/ */
export default function SplitEditor({ note, error, splitOptions, previewContent, previewButtons, className, ...editorProps }: SplitEditorProps) { export default function SplitEditor({ note, error, splitOptions, previewContent, previewButtons, className, editorBefore, ...editorProps }: SplitEditorProps) {
const splitEditorOrientation = useSplitOrientation(); const splitEditorOrientation = useSplitOrientation();
const [ readOnly ] = useNoteLabelBoolean(note, "readOnly"); const [ readOnly ] = useNoteLabelBoolean(note, "readOnly");
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const editor = (!readOnly && const editor = (!readOnly &&
<div className="note-detail-split-editor-col"> <div className="note-detail-split-editor-col">
{editorBefore}
<div className="note-detail-split-editor"> <div className="note-detail-split-editor">
<EditableCode <EditableCode
note={note} note={note}
@ -74,12 +78,12 @@ export default function SplitEditor({ note, error, splitOptions, previewContent,
}, [ readOnly, splitEditorOrientation ]); }, [ readOnly, splitEditorOrientation ]);
return ( return (
<div ref={containerRef} className={`note-detail-split note-detail-printable ${"split-" + splitEditorOrientation} ${readOnly ? "split-read-only" : ""} ${className ?? ""}`}> <div ref={containerRef} className={`note-detail-split note-detail-printable ${`split-${ splitEditorOrientation}`} ${readOnly ? "split-read-only" : ""} ${className ?? ""}`}>
{splitEditorOrientation === "horizontal" {splitEditorOrientation === "horizontal"
? <>{editor}{preview}</> ? <>{editor}{preview}</>
: <>{preview}{editor}</>} : <>{preview}{editor}</>}
</div> </div>
) );
} }
export function PreviewButton(props: Omit<ActionButtonProps, "titlePosition">) { export function PreviewButton(props: Omit<ActionButtonProps, "titlePosition">) {
@ -88,7 +92,7 @@ export function PreviewButton(props: Omit<ActionButtonProps, "titlePosition">) {
className="tn-tool-button" className="tn-tool-button"
noIconActionClass noIconActionClass
titlePosition="top" titlePosition="top"
/> />;
} }
function useSplitOrientation() { function useSplitOrientation() {