@quantumwake/terminal-ux-dashboard-components 0.1.5 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,6 +8,9 @@ import { ResponsiveHeatMap } from '@nivo/heatmap';
8
8
  import PivotTableUI from 'react-pivottable/PivotTableUI';
9
9
  import 'react-pivottable/pivottable.css';
10
10
  import { Loader2, Play, ChevronDown, AlertCircle, Rows3, BarChart3, PieChart, TrendingUp, ScatterChart, LayoutGrid, Code, Sparkles, X, Terminal, RefreshCw, Minimize2, Maximize2, Plus, Save, FolderOpen, Trash2, Filter, Database, Send, GripVertical } from 'lucide-react';
11
+ import CodeMirror, { EditorView, Prec, keymap } from '@uiw/react-codemirror';
12
+ import { sql, PostgreSQL, schemaCompletionSource, keywordCompletionSource } from '@codemirror/lang-sql';
13
+ import { snippetCompletion, completeFromList, autocompletion, acceptCompletion } from '@codemirror/autocomplete';
11
14
 
12
15
  // src/context/DashboardContext.tsx
13
16
  var DashboardContext = createContext(null);
@@ -636,6 +639,280 @@ function ChartStyleControls({ style, onChange }) {
636
639
  ] })
637
640
  ] });
638
641
  }
642
+ var AGGREGATES = [
643
+ "count",
644
+ "count_if",
645
+ "sum",
646
+ "avg",
647
+ "min",
648
+ "max",
649
+ "median",
650
+ "mode",
651
+ "stddev",
652
+ "stddev_pop",
653
+ "stddev_samp",
654
+ "var_pop",
655
+ "var_samp",
656
+ "variance",
657
+ "quantile",
658
+ "quantile_cont",
659
+ "quantile_disc",
660
+ "approx_count_distinct",
661
+ "approx_quantile",
662
+ "arg_max",
663
+ "arg_min",
664
+ "first",
665
+ "last",
666
+ "product",
667
+ "bool_and",
668
+ "bool_or",
669
+ "bit_and",
670
+ "bit_or",
671
+ "bit_xor",
672
+ "string_agg",
673
+ "list",
674
+ "array_agg",
675
+ "histogram",
676
+ "corr",
677
+ "covar_pop",
678
+ "covar_samp",
679
+ "kurtosis",
680
+ "skewness",
681
+ "entropy",
682
+ "row_number",
683
+ "rank",
684
+ "dense_rank",
685
+ "percent_rank",
686
+ "cume_dist",
687
+ "ntile",
688
+ "lag",
689
+ "lead",
690
+ "first_value",
691
+ "last_value",
692
+ "nth_value"
693
+ ];
694
+ var SCALARS = [
695
+ // string
696
+ "length",
697
+ "len",
698
+ "lower",
699
+ "upper",
700
+ "trim",
701
+ "ltrim",
702
+ "rtrim",
703
+ "substring",
704
+ "substr",
705
+ "concat",
706
+ "concat_ws",
707
+ "replace",
708
+ "reverse",
709
+ "repeat",
710
+ "lpad",
711
+ "rpad",
712
+ "left",
713
+ "right",
714
+ "contains",
715
+ "starts_with",
716
+ "ends_with",
717
+ "position",
718
+ "split_part",
719
+ "string_split",
720
+ "regexp_matches",
721
+ "regexp_replace",
722
+ "regexp_extract",
723
+ "regexp_full_match",
724
+ "regexp_split_to_array",
725
+ "format",
726
+ "printf",
727
+ "md5",
728
+ "sha256",
729
+ "levenshtein",
730
+ "jaccard",
731
+ "ascii",
732
+ "chr",
733
+ // date / time
734
+ "now",
735
+ "current_date",
736
+ "current_timestamp",
737
+ "today",
738
+ "date_trunc",
739
+ "date_part",
740
+ "date_diff",
741
+ "date_add",
742
+ "date_sub",
743
+ "age",
744
+ "strftime",
745
+ "strptime",
746
+ "epoch",
747
+ "epoch_ms",
748
+ "extract",
749
+ "make_date",
750
+ "make_time",
751
+ "make_timestamp",
752
+ "last_day",
753
+ "dayname",
754
+ "monthname",
755
+ "year",
756
+ "month",
757
+ "day",
758
+ "hour",
759
+ "minute",
760
+ "second",
761
+ "dayofweek",
762
+ "dayofyear",
763
+ "week",
764
+ "quarter",
765
+ "time_bucket",
766
+ // math
767
+ "abs",
768
+ "ceil",
769
+ "ceiling",
770
+ "floor",
771
+ "round",
772
+ "trunc",
773
+ "sign",
774
+ "mod",
775
+ "pow",
776
+ "power",
777
+ "sqrt",
778
+ "cbrt",
779
+ "exp",
780
+ "ln",
781
+ "log",
782
+ "log2",
783
+ "log10",
784
+ "greatest",
785
+ "least",
786
+ "gcd",
787
+ "lcm",
788
+ "factorial",
789
+ "random",
790
+ "pi",
791
+ "degrees",
792
+ "radians",
793
+ "sin",
794
+ "cos",
795
+ "tan",
796
+ "asin",
797
+ "acos",
798
+ "atan",
799
+ "atan2",
800
+ // conditional / null / cast
801
+ "coalesce",
802
+ "ifnull",
803
+ "nullif",
804
+ "nvl",
805
+ "if",
806
+ "try_cast",
807
+ "cast",
808
+ "typeof",
809
+ // list / struct / map / json
810
+ "list_value",
811
+ "list_aggregate",
812
+ "list_distinct",
813
+ "list_sort",
814
+ "list_reverse",
815
+ "list_slice",
816
+ "list_concat",
817
+ "list_contains",
818
+ "list_position",
819
+ "list_extract",
820
+ "list_transform",
821
+ "list_filter",
822
+ "unnest",
823
+ "array_length",
824
+ "array_to_string",
825
+ "struct_pack",
826
+ "struct_extract",
827
+ "map",
828
+ "map_keys",
829
+ "map_values",
830
+ "json_extract",
831
+ "json_extract_string",
832
+ "to_json",
833
+ "from_json",
834
+ "json_array_length",
835
+ "json_keys",
836
+ "generate_series",
837
+ "range"
838
+ ];
839
+ var TYPES = [
840
+ "BOOLEAN",
841
+ "TINYINT",
842
+ "SMALLINT",
843
+ "INTEGER",
844
+ "BIGINT",
845
+ "HUGEINT",
846
+ "UTINYINT",
847
+ "USMALLINT",
848
+ "UINTEGER",
849
+ "UBIGINT",
850
+ "FLOAT",
851
+ "REAL",
852
+ "DOUBLE",
853
+ "DECIMAL",
854
+ "NUMERIC",
855
+ "VARCHAR",
856
+ "CHAR",
857
+ "TEXT",
858
+ "BLOB",
859
+ "BIT",
860
+ "DATE",
861
+ "TIME",
862
+ "TIMESTAMP",
863
+ "TIMESTAMPTZ",
864
+ "INTERVAL",
865
+ "UUID",
866
+ "JSON",
867
+ "LIST",
868
+ "ARRAY",
869
+ "STRUCT",
870
+ "MAP",
871
+ "UNION",
872
+ "ENUM"
873
+ ];
874
+ var EXTRA_KEYWORDS = [
875
+ "QUALIFY",
876
+ "EXCLUDE",
877
+ "PIVOT",
878
+ "UNPIVOT",
879
+ "ASOF",
880
+ "POSITIONAL",
881
+ "SEMI",
882
+ "ANTI",
883
+ "SUMMARIZE",
884
+ "DISTINCT ON",
885
+ "GROUP BY ALL",
886
+ "ORDER BY ALL",
887
+ "USING SAMPLE"
888
+ ];
889
+ function opts(labels, type, boost = 0) {
890
+ return labels.map((label) => ({ label, type, boost }));
891
+ }
892
+ var SNIPPETS = [
893
+ snippetCompletion("SELECT ${col}, count(*) AS n\nFROM data\nGROUP BY ${col}\nORDER BY n DESC", {
894
+ label: "group by count",
895
+ type: "snippet",
896
+ detail: "aggregate by column"
897
+ }),
898
+ snippetCompletion("SELECT *\nFROM data\nORDER BY ${col} DESC\nLIMIT 10", {
899
+ label: "top N",
900
+ type: "snippet",
901
+ detail: "order + limit"
902
+ }),
903
+ snippetCompletion("SELECT DISTINCT ${col}\nFROM data", {
904
+ label: "select distinct",
905
+ type: "snippet"
906
+ })
907
+ ];
908
+ var duckdbCompletionSource = completeFromList([
909
+ ...opts(AGGREGATES, "function", 1),
910
+ // boost aggregates slightly — common in analysis
911
+ ...opts(SCALARS, "function"),
912
+ ...opts(TYPES, "type"),
913
+ ...opts(EXTRA_KEYWORDS, "keyword"),
914
+ ...SNIPPETS
915
+ ]);
639
916
  var DEFAULT_SQL = "SELECT *\nFROM data\nLIMIT 100";
640
917
  var renderCell = (v) => {
641
918
  if (v == null) return "";
@@ -645,12 +922,12 @@ var renderCell = (v) => {
645
922
  };
646
923
  function SqlConsole({ columns = [], stateId, defaultColumnsOpen = false, defaultSql = DEFAULT_SQL, autoRun = false }) {
647
924
  const { theme, runQuery } = useDashboard();
648
- const [sql, setSql] = useState(defaultSql);
925
+ const [sql$1, setSql] = useState(defaultSql);
649
926
  const [result, setResult] = useState(null);
650
927
  const [error, setError] = useState(null);
651
928
  const [running, setRunning] = useState(false);
652
929
  const [columnsOpen, setColumnsOpen] = useState(defaultColumnsOpen);
653
- const run = async (text = sql) => {
930
+ const run = async (text = sql$1) => {
654
931
  if (running || !text.trim()) return;
655
932
  setRunning(true);
656
933
  setError(null);
@@ -669,12 +946,38 @@ function SqlConsole({ columns = [], stateId, defaultColumnsOpen = false, default
669
946
  setSql(defaultSql);
670
947
  void run(defaultSql);
671
948
  }, [stateId, autoRun, defaultSql]);
672
- const onKeyDown = (e) => {
673
- if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
674
- e.preventDefault();
675
- void run();
676
- }
949
+ const runRef = useRef(() => {
950
+ });
951
+ runRef.current = () => {
952
+ void run();
677
953
  };
954
+ const schemaKey = columns.map((c) => c.name).join(",");
955
+ const extensions = useMemo(() => {
956
+ const sqlConfig = {
957
+ dialect: PostgreSQL,
958
+ schema: { data: columns.map((c) => c.name) },
959
+ defaultTable: "data",
960
+ upperCaseKeywords: true
961
+ };
962
+ return [
963
+ sql(sqlConfig),
964
+ autocompletion({
965
+ override: [
966
+ schemaCompletionSource(sqlConfig),
967
+ keywordCompletionSource(PostgreSQL, true),
968
+ duckdbCompletionSource
969
+ ]
970
+ }),
971
+ EditorView.lineWrapping,
972
+ Prec.highest(keymap.of([
973
+ { key: "Mod-Enter", run: () => {
974
+ runRef.current();
975
+ return true;
976
+ } },
977
+ { key: "Tab", run: acceptCompletion }
978
+ ]))
979
+ ];
980
+ }, [schemaKey]);
678
981
  const resultColumns = result?.columns || [];
679
982
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
680
983
  /* @__PURE__ */ jsxs("div", { className: `border-b ${theme.border} bg-midnight-elevated p-3 shrink-0`, children: [
@@ -687,7 +990,7 @@ function SqlConsole({ columns = [], stateId, defaultColumnsOpen = false, default
687
990
  "button",
688
991
  {
689
992
  onClick: () => void run(),
690
- disabled: running || !sql.trim(),
993
+ disabled: running || !sql$1.trim(),
691
994
  className: `flex items-center gap-1.5 px-3 py-1 border text-xs font-mono transition-colors ${running ? "border-midnight-border opacity-50" : "border-midnight-accent text-midnight-accent hover:bg-midnight-accent/10"}`,
692
995
  children: [
693
996
  running ? /* @__PURE__ */ jsx(Loader2, { className: "w-3.5 h-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Play, { className: "w-3.5 h-3.5" }),
@@ -698,15 +1001,16 @@ function SqlConsole({ columns = [], stateId, defaultColumnsOpen = false, default
698
1001
  )
699
1002
  ] }),
700
1003
  /* @__PURE__ */ jsx(
701
- "textarea",
1004
+ CodeMirror,
702
1005
  {
703
- value: sql,
704
- onChange: (e) => setSql(e.target.value),
705
- onKeyDown,
706
- spellCheck: false,
707
- rows: 5,
708
- className: "w-full bg-midnight-surface border border-midnight-border px-3 py-2 text-sm font-mono outline-none text-midnight-text-body resize-y focus:border-midnight-accent transition-colors",
709
- placeholder: "SELECT ... FROM data"
1006
+ value: sql$1,
1007
+ onChange: setSql,
1008
+ extensions,
1009
+ theme: "dark",
1010
+ basicSetup: { lineNumbers: false, foldGutter: false, highlightActiveLine: false, autocompletion: false },
1011
+ height: "120px",
1012
+ placeholder: "SELECT ... FROM data",
1013
+ className: "border border-midnight-border text-sm overflow-hidden focus-within:border-midnight-accent"
710
1014
  }
711
1015
  ),
712
1016
  columns.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [