@graphit/cli 0.1.15 → 0.1.18
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/commands/dashboard.js +79 -3
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/ds.js +24 -0
- package/dist/commands/ds.js.map +1 -1
- package/dist/commands/governance.d.ts +2 -0
- package/dist/commands/governance.js +77 -0
- package/dist/commands/governance.js.map +1 -0
- package/dist/commands/kb.js +76 -3
- package/dist/commands/kb.js.map +1 -1
- package/dist/commands/query.js +73 -2
- package/dist/commands/query.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/output/format.d.ts +1 -1
- package/dist/output/format.js +2 -0
- package/dist/output/format.js.map +1 -1
- package/dist/output/styled.d.ts +19 -0
- package/dist/output/styled.js +70 -0
- package/dist/output/styled.js.map +1 -0
- package/package.json +3 -1
- package/skills/graphit/SKILL.md +95 -42
- package/skills/graphit/cursor/graphit-dashboard-planning.mdc +5 -0
- package/skills/graphit/cursor/graphit-kb-traversal.mdc +7 -0
- package/skills/graphit/cursor/graphit-sql-reference.mdc +33 -0
- package/skills/graphit/cursor/graphit-style.mdc +16 -0
- package/skills/graphit/graphit.mdc +83 -10
- package/skills/graphit/references/dashboard-planning.md +24 -0
- package/skills/graphit/references/governance.md +106 -0
- package/skills/graphit/references/graphit-style.md +16 -0
- package/skills/graphit/references/kb-traversal.md +65 -0
- package/skills/graphit/references/sql-reference.md +106 -0
package/dist/index.js
CHANGED
|
@@ -7,19 +7,21 @@ import { registerDSCommands } from "./commands/ds.js";
|
|
|
7
7
|
import { registerDashboardCommands } from "./commands/dashboard.js";
|
|
8
8
|
import { registerConnectorCommands } from "./commands/connector.js";
|
|
9
9
|
import { registerSetupCommand } from "./commands/setup.js";
|
|
10
|
+
import { registerGovernanceCommands } from "./commands/governance.js";
|
|
10
11
|
import { getCurrentVersion, printUpdateBanner, fireBackgroundCheck, } from "./update-check.js";
|
|
11
12
|
const program = new Command();
|
|
12
13
|
program
|
|
13
14
|
.name("graphit")
|
|
14
15
|
.description("Graphit CLI - Build custom dashboards from any AI coding assistant")
|
|
15
16
|
.version(getCurrentVersion())
|
|
16
|
-
.option("--output <format>", "Output format: json (default) or
|
|
17
|
+
.option("--output <format>", "Output format: json (default), table, or styled", "json");
|
|
17
18
|
registerAuthCommands(program);
|
|
18
19
|
registerKBCommands(program);
|
|
19
20
|
registerQueryCommands(program);
|
|
20
21
|
registerDSCommands(program);
|
|
21
22
|
registerDashboardCommands(program);
|
|
22
23
|
registerConnectorCommands(program);
|
|
24
|
+
registerGovernanceCommands(program);
|
|
23
25
|
registerSetupCommand(program);
|
|
24
26
|
printUpdateBanner();
|
|
25
27
|
program.parse();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,oEAAoE,CAAC;KACjF,OAAO,CAAC,iBAAiB,EAAE,CAAC;KAC5B,MAAM,CAAC,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,oEAAoE,CAAC;KACjF,OAAO,CAAC,iBAAiB,EAAE,CAAC;KAC5B,MAAM,CAAC,mBAAmB,EAAE,iDAAiD,EAAE,MAAM,CAAC,CAAC;AAE1F,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,yBAAyB,CAAC,OAAO,CAAC,CAAC;AACnC,yBAAyB,CAAC,OAAO,CAAC,CAAC;AACnC,0BAA0B,CAAC,OAAO,CAAC,CAAC;AACpC,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAE9B,iBAAiB,EAAE,CAAC;AACpB,OAAO,CAAC,KAAK,EAAE,CAAC;AAChB,mBAAmB,EAAE,CAAC"}
|
package/dist/output/format.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
export type OutputFormat = "json" | "table";
|
|
2
|
+
export type OutputFormat = "json" | "table" | "styled";
|
|
3
3
|
export declare function getOutputFormat(cmd: Command): OutputFormat;
|
|
4
4
|
export declare function output(cmd: Command, data: unknown, opts?: {
|
|
5
5
|
columns?: string[];
|
package/dist/output/format.js
CHANGED
|
@@ -3,6 +3,8 @@ import { formatTable, formatKeyValue } from "./table.js";
|
|
|
3
3
|
export function getOutputFormat(cmd) {
|
|
4
4
|
const root = cmd.parent ?? cmd;
|
|
5
5
|
const format = root.opts().output;
|
|
6
|
+
if (format === "styled")
|
|
7
|
+
return "styled";
|
|
6
8
|
return format === "table" ? "table" : "json";
|
|
7
9
|
}
|
|
8
10
|
export function output(cmd, data, opts) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/output/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIzD,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAgB,CAAC;IAC5C,OAAO,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,MAAM,CACpB,GAAY,EACZ,IAAa,EACb,IAAiD;IAEjD,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAEpC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,IAAI,IAAI,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAA+B,CAAC,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAiC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,IAAI,OAAe,CAAC;IACpB,IAAI,UAA8B,CAAC;IAEnC,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IACxB,CAAC;SAAM,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACnD,MAAM,GAAG,GAAG,GAA8B,CAAC;QAC3C,OAAO,GAAI,GAAG,CAAC,MAAiB,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,OAAO,GAAG,CAAC,mBAAmB,KAAK,QAAQ,EAAE,CAAC;YAChD,UAAU,GAAG,GAAG,CAAC,mBAAmB,CAAC;QACvC,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,GAAG,GAAG,OAAO,aAAa,UAAU,IAAI,CAAC;IAClD,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/output/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIzD,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAgB,CAAC;IAC5C,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACzC,OAAO,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,MAAM,CACpB,GAAY,EACZ,IAAa,EACb,IAAiD;IAEjD,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAEpC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,IAAI,IAAI,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAA+B,CAAC,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,IAAiC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,IAAI,OAAe,CAAC;IACpB,IAAI,UAA8B,CAAC;IAEnC,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IACxB,CAAC;SAAM,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACnD,MAAM,GAAG,GAAG,GAA8B,CAAC;QAC3C,OAAO,GAAI,GAAG,CAAC,MAAiB,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;QAChD,IAAI,OAAO,GAAG,CAAC,mBAAmB,KAAK,QAAQ,EAAE,CAAC;YAChD,UAAU,GAAG,GAAG,CAAC,mBAAmB,CAAC;QACvC,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,GAAG,GAAG,OAAO,aAAa,UAAU,IAAI,CAAC;IAClD,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare function styledIntro(command: string): void;
|
|
2
|
+
export declare function styledOutro(message?: string): void;
|
|
3
|
+
export declare function styledWarn(message: string): void;
|
|
4
|
+
export declare function styledError(message: string): void;
|
|
5
|
+
export declare function styledSuccess(message: string): void;
|
|
6
|
+
export declare function styledInfo(message: string): void;
|
|
7
|
+
export declare function styledMessage(content: string): void;
|
|
8
|
+
export declare function styledNote(content: string, title: string): void;
|
|
9
|
+
export declare function styledTable(headers: string[], rows: (string | number | null | undefined)[][], opts?: {
|
|
10
|
+
rightAlign?: number[];
|
|
11
|
+
}): string;
|
|
12
|
+
export declare function styledKeyValue(pairs: [string, string][]): string;
|
|
13
|
+
export declare function formatBold(text: string): string;
|
|
14
|
+
export declare function formatDim(text: string): string;
|
|
15
|
+
export declare function formatCyan(text: string): string;
|
|
16
|
+
export declare function formatGreen(text: string): string;
|
|
17
|
+
export declare function formatYellow(text: string): string;
|
|
18
|
+
export declare function formatRed(text: string): string;
|
|
19
|
+
export declare function formatSeparator(): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
export function styledIntro(command) {
|
|
4
|
+
p.intro(chalk.bgCyan.black(` ${command} `));
|
|
5
|
+
}
|
|
6
|
+
export function styledOutro(message = "Done") {
|
|
7
|
+
p.outro(message);
|
|
8
|
+
}
|
|
9
|
+
export function styledWarn(message) {
|
|
10
|
+
p.log.warn(message);
|
|
11
|
+
}
|
|
12
|
+
export function styledError(message) {
|
|
13
|
+
p.log.error(message);
|
|
14
|
+
}
|
|
15
|
+
export function styledSuccess(message) {
|
|
16
|
+
p.log.success(message);
|
|
17
|
+
}
|
|
18
|
+
export function styledInfo(message) {
|
|
19
|
+
p.log.info(message);
|
|
20
|
+
}
|
|
21
|
+
export function styledMessage(content) {
|
|
22
|
+
p.log.message(content);
|
|
23
|
+
}
|
|
24
|
+
export function styledNote(content, title) {
|
|
25
|
+
p.note(content, title);
|
|
26
|
+
}
|
|
27
|
+
export function styledTable(headers, rows, opts) {
|
|
28
|
+
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => String(r[i] ?? "").length)));
|
|
29
|
+
const headerLine = headers
|
|
30
|
+
.map((h, i) => chalk.cyan.bold(h.padEnd(widths[i])))
|
|
31
|
+
.join(" ");
|
|
32
|
+
const sep = widths.map((w) => "─".repeat(w)).join(" ");
|
|
33
|
+
const body = rows.map((row) => row
|
|
34
|
+
.map((cell, i) => {
|
|
35
|
+
const s = String(cell ?? "");
|
|
36
|
+
return opts?.rightAlign?.includes(i)
|
|
37
|
+
? s.padStart(widths[i])
|
|
38
|
+
: s.padEnd(widths[i]);
|
|
39
|
+
})
|
|
40
|
+
.join(" "));
|
|
41
|
+
return [headerLine, chalk.dim(sep), ...body].join("\n");
|
|
42
|
+
}
|
|
43
|
+
export function styledKeyValue(pairs) {
|
|
44
|
+
const maxKey = Math.max(...pairs.map(([k]) => k.length));
|
|
45
|
+
return pairs
|
|
46
|
+
.map(([k, v]) => `${chalk.dim(k.padStart(maxKey))} ${v}`)
|
|
47
|
+
.join("\n");
|
|
48
|
+
}
|
|
49
|
+
export function formatBold(text) {
|
|
50
|
+
return chalk.bold(text);
|
|
51
|
+
}
|
|
52
|
+
export function formatDim(text) {
|
|
53
|
+
return chalk.dim(text);
|
|
54
|
+
}
|
|
55
|
+
export function formatCyan(text) {
|
|
56
|
+
return chalk.cyan(text);
|
|
57
|
+
}
|
|
58
|
+
export function formatGreen(text) {
|
|
59
|
+
return chalk.green(text);
|
|
60
|
+
}
|
|
61
|
+
export function formatYellow(text) {
|
|
62
|
+
return chalk.yellow(text);
|
|
63
|
+
}
|
|
64
|
+
export function formatRed(text) {
|
|
65
|
+
return chalk.red(text);
|
|
66
|
+
}
|
|
67
|
+
export function formatSeparator() {
|
|
68
|
+
return chalk.dim(" · ");
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=styled.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styled.js","sourceRoot":"","sources":["../../src/output/styled.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphit/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Graphit CLI - Build custom dashboards from any AI coding assistant",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"license": "UNLICENSED",
|
|
31
31
|
"dependencies": {
|
|
32
|
+
"@clack/prompts": "^1.5.1",
|
|
33
|
+
"chalk": "^5.6.2",
|
|
32
34
|
"commander": "^13.0.0",
|
|
33
35
|
"open": "^10.0.0"
|
|
34
36
|
},
|
package/skills/graphit/SKILL.md
CHANGED
|
@@ -54,7 +54,9 @@ NEVER query the warehouse directly when a cached data source covers the table. D
|
|
|
54
54
|
3. Only use `graphit query "SQL" --warehouse --connection <id>` if NO data source exists and the user approves
|
|
55
55
|
|
|
56
56
|
**Wrong:** Jumping straight to `graphit query "SELECT SUM(cost) FROM marketing_ua" --warehouse` when a data source already caches that table.
|
|
57
|
-
**Right:** `graphit ds list` first, find the DS ID, then `graphit query "SELECT SUM(cost) FROM
|
|
57
|
+
**Right:** `graphit ds list` first, find the DS ID, then `graphit query "SELECT SUM(cost) FROM marketing_ua_ds" --ds ds_abc123`.
|
|
58
|
+
|
|
59
|
+
**Use the data source name in SQL** (e.g. `FROM MARKETING_UA_DS`), not the raw source table. The DS is a KB table - governance rules target it directly, and users recognize DS names from the platform.
|
|
58
60
|
|
|
59
61
|
### 3. EVERY element must have entity wrapping
|
|
60
62
|
Without `data-graphit-*` attributes, elements are invisible to the platform - no click info, no mentions, no KB provenance. Every chart, KPI card, table, and text section needs ALL FIVE attributes:
|
|
@@ -63,7 +65,7 @@ Without `data-graphit-*` attributes, elements are invisible to the platform - no
|
|
|
63
65
|
<div data-graphit-id="revenue-trend"
|
|
64
66
|
data-graphit-label="Revenue Trend"
|
|
65
67
|
data-graphit-kb="metric:REVENUE,dimension:REGION,table:ORDERS"
|
|
66
|
-
data-graphit-sql="SELECT region, SUM(revenue) FROM
|
|
68
|
+
data-graphit-sql="SELECT region, SUM(revenue) FROM orders GROUP BY region"
|
|
67
69
|
data-graphit-ds="ds_abc123">
|
|
68
70
|
<!-- chart/KPI/table content here -->
|
|
69
71
|
</div>
|
|
@@ -73,21 +75,32 @@ Without `data-graphit-*` attributes, elements are invisible to the platform - no
|
|
|
73
75
|
|-----------|--------|---------|
|
|
74
76
|
| `data-graphit-id` | Unique kebab-case | `"spend-by-source"` |
|
|
75
77
|
| `data-graphit-label` | Human-readable name | `"Ad Spend by Source"` |
|
|
76
|
-
| `data-graphit-kb` | `type:NAME` comma-separated | `"metric:CPI,dimension:MEDIA_SOURCE,table:
|
|
78
|
+
| `data-graphit-kb` | `type:NAME` comma-separated | `"metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA_DS"` |
|
|
77
79
|
| `data-graphit-sql` | Executable SQL (HTML-encode `<>&"`) | `"SELECT ..."` |
|
|
78
80
|
| `data-graphit-ds` | Data source ID from `graphit ds list` | `"ds_abc123"` |
|
|
79
81
|
|
|
80
82
|
KB types: `metric`, `dimension`, `table`, `rule`. Names are UPPER_SNAKE_CASE matching the KB exactly. **Names are unique per type per org** - creating a duplicate name will fail. Check `graphit kb list <type>` first; update the existing asset instead of creating a new one.
|
|
81
83
|
Missing any attribute = broken entity. Missing wrapping entirely = invisible to the platform.
|
|
82
84
|
|
|
83
|
-
**SQL must be executable.** The platform runs `data-graphit-sql` against the data source when a user opens the entity's details panel. It MUST use the real DS table name and only columns that exist in the DS - never invented summary columns, CTE aliases, JS variable names, or prose. If the chart's resolve call uses a CTE, put the full WITH query. If JS builds SQL dynamically, store one representative executable variant (e.g. the default date range).
|
|
85
|
+
**SQL must be complete and executable.** The platform runs `data-graphit-sql` against the data source when a user opens the entity's details panel. Write the full query from the `graphit.resolve()` call - never abbreviate, truncate, or use placeholders (`FROM ...`, `SELECT ...`, ellipsis). It MUST use the real DS table name and only columns that exist in the DS - never invented summary columns, CTE aliases, JS variable names, or prose. If the chart's resolve call uses a CTE, put the full WITH query. If JS builds SQL dynamically, store one representative executable variant (e.g. the default date range).
|
|
84
86
|
|
|
85
87
|
**Wrong:** `data-graphit-sql="SELECT INSTALL_TIME, ROIAP_D0, ROIAP_D3 FROM UA_DS"` when the DS has no ROIAP_D0 column (the chart computes it via CASE) - the details panel errors.
|
|
86
88
|
**Right:** `data-graphit-sql="SELECT INSTALL_TIME, SUM(CASE WHEN SENIORITY=0 THEN TOTAL_IAP END)/NULLIF(SUM(COST),0) AS ROIAP_D0 FROM UA_DS GROUP BY 1"` - same derivation the chart runs.
|
|
87
89
|
|
|
88
90
|
**Label = visible title.** The `data-graphit-label` MUST match the card's visible heading exactly. Users see the label in @ mention dropdowns and entity panels - if it doesn't match the title on screen, they can't find their chart.
|
|
89
91
|
|
|
90
|
-
### 4.
|
|
92
|
+
### 4. Shared dashboards require Edit mode on the platform
|
|
93
|
+
Dashboards shared with teams are protected by an editing session lock. The CLI cannot acquire editing sessions - mutations (`dashboard update-html`, `dashboard update-entity`) on a shared dashboard will fail with **423: "Shared dashboard requires an active editing session"**.
|
|
94
|
+
|
|
95
|
+
**When you see a 423 from a dashboard command:**
|
|
96
|
+
1. Tell the user to open the dashboard on the Graphit platform and click the **Edit** button in the header
|
|
97
|
+
2. Once they're in edit mode (draft active), CLI mutations will work because the session is held
|
|
98
|
+
3. When done, the user publishes or discards from the platform UI
|
|
99
|
+
4. Private dashboards (not shared with any team) are unaffected - the CLI can mutate them directly
|
|
100
|
+
|
|
101
|
+
**Note:** another team member may already hold the editing session. Only one editor at a time. The user can check who's editing on the platform.
|
|
102
|
+
|
|
103
|
+
### 5. ALWAYS use graphit.resolve() for live data
|
|
91
104
|
NEVER embed query results as static JS variables. The dashboard iframe provides `graphit.resolve()` which fetches live data from cached data sources on every page load.
|
|
92
105
|
|
|
93
106
|
**Wrong:** Running queries at build time, embedding results as `const data = [{...}, ...]` in the HTML.
|
|
@@ -193,7 +206,14 @@ For metrics with natural variant axes (D7/D30/D90, gross/net), propose parameter
|
|
|
193
206
|
| `graphit kb delete <type> <name> --yes` | Delete any entity (all types supported) |
|
|
194
207
|
| `graphit ds list` | List cached data sources (use these for fast queries) |
|
|
195
208
|
| `graphit query "<sql>" --ds <id>` | Query cached data source (~100ms) |
|
|
209
|
+
| `graphit query "<sql>" --ds <id> --override-rules RULE1 RULE2` | Query with governance rule overrides |
|
|
210
|
+
| `graphit query "<sql>" --ds <id> --verbose` | Show expanded SQL and trust tier |
|
|
196
211
|
| `graphit query "<sql>" --warehouse --connection <id>` | Query live Snowflake (~10s) |
|
|
212
|
+
| `graphit governance status` | Show governance mode and conformance stats |
|
|
213
|
+
| `graphit governance set <mode>` | Set governance mode (observe/warn/strict) |
|
|
214
|
+
| `graphit governance audit` | View recent governance audit events |
|
|
215
|
+
| `graphit ds update <id> --governed-mode on\|off` | Enable/disable governed mode on a data source |
|
|
216
|
+
| `graphit ds update <id> --max-rows N` | Set max rows cap on a data source |
|
|
197
217
|
| `graphit dashboard create --name "..."` | Create dashboard (returns ID) |
|
|
198
218
|
| `graphit dashboard get-html <id>` | Get current HTML content of a dashboard |
|
|
199
219
|
| `graphit dashboard update-html <id> --file <path>` | Upload HTML to dashboard (full page replace) |
|
|
@@ -204,42 +224,49 @@ For metrics with natural variant axes (D7/D30/D90, gross/net), propose parameter
|
|
|
204
224
|
|
|
205
225
|
## Presenting Results to the User
|
|
206
226
|
|
|
207
|
-
The user CANNOT see raw CLI output
|
|
227
|
+
The user CANNOT see raw CLI JSON output. You are the rendering layer - format and present every result using markdown. Per-command templates are in the reference file for each domain: `sql-reference.md` (query + DS results), `kb-traversal.md` (KB list/get/search/explore), `governance.md` (governance status + errors), `dashboard-planning.md` (dashboard + connector lists).
|
|
208
228
|
|
|
209
|
-
**
|
|
210
|
-
```
|
|
211
|
-
Queried **MARKETING_UA** (ds_abc123, 6 rows):
|
|
229
|
+
**Core rules:** Ground every result in the KB - list referenced metrics/dimensions/tables, show both the `{{metric:X}}` query and resolved SQL, always use `--verbose`. Bold every KB asset/table/DS name. Markdown tables for tabular data. SQL in ```sql code blocks. Format numbers (commas, $, %). Governance provenance footer after every query. For ad-hoc queries, suggest the KB reference equivalent. Never silently consume output.
|
|
212
230
|
|
|
213
|
-
|
|
214
|
-
|-------------|----------|----------|-------|
|
|
215
|
-
| Facebook | $42,100 | 12,400 | $3.40 |
|
|
216
|
-
| Google UAC | $38,500 | 9,800 | $3.93 |
|
|
217
|
-
| TikTok | $21,300 | 8,200 | $2.60 |
|
|
218
|
-
```
|
|
219
|
-
```
|
|
231
|
+
---
|
|
220
232
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
233
|
+
## Query Governance and Reference Syntax
|
|
234
|
+
|
|
235
|
+
The platform enforces server-side query governance. Use KB reference syntax for governed queries:
|
|
236
|
+
|
|
237
|
+
| Syntax | Expands To | Example |
|
|
238
|
+
|--------|-----------|---------|
|
|
239
|
+
| `{{metric:NAME}}` | Metric calculation (aggregation) | `{{metric:CPI}}` |
|
|
240
|
+
| `{{metric:NAME(K=V)}}` | Parameterized metric | `{{metric:ARPU(DAY=7)}}` |
|
|
241
|
+
| `{{metric_raw:NAME}}` | Raw expression (no aggregate) | `{{metric_raw:REVENUE}}` |
|
|
242
|
+
| `{{dim:NAME}}` | Dimension expression | `{{dim:INSTALL_MONTH}}` |
|
|
243
|
+
|
|
244
|
+
**Parameterized metrics:** Some metrics (ARPU, ROAS, RETENTION) contain `${PARAM:X}` tokens and require explicit parameter values. Run `graphit kb list metric` - the `params` column shows required parameter names. Use `{{metric:ARPU(DAY=7)}}` to resolve `${PARAM:DAY}` to `7`. Omitting required parameters returns a clear error with the exact syntax to use. Pre-baked variants (ARPU_D7, ROAS_D30) have the value hardcoded and need no parameters.
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# Governed query using reference syntax
|
|
248
|
+
graphit query "SELECT {{dim:INSTALL_MONTH}}, {{metric:CPI}} as cpi FROM MARKETING_UA_DS GROUP BY 1" --ds ds_abc123
|
|
249
|
+
|
|
250
|
+
# Parameterized metric
|
|
251
|
+
graphit query "SELECT {{dim:INSTALL_MONTH}}, {{metric:ARPU(DAY=7)}} as arpu FROM MARKETING_UA_DS GROUP BY 1" --ds ds_abc123
|
|
252
|
+
|
|
253
|
+
# Verbose shows expanded SQL and trust tier
|
|
254
|
+
graphit query "SELECT {{metric:CPI}} as cpi FROM MARKETING_UA_DS" --ds ds_abc123 --verbose
|
|
227
255
|
```
|
|
228
256
|
|
|
229
|
-
**
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
- Skip showing KB exploration results before proposing a dashboard
|
|
257
|
+
Queries using reference syntax produce **governed** trust tier. Inline formulas produce **ad-hoc** tier, which may be blocked in strict mode. Use `--override-rules RULE_NAME` to bypass specific enforceable rules when the user explicitly requests it.
|
|
258
|
+
|
|
259
|
+
Trust tiers: **governed** (used KB references), **verified** (raw SQL matches KB definitions), **ad-hoc** (inline formulas). Badges appear on dashboard graphs and canvas entities.
|
|
260
|
+
|
|
261
|
+
Use reference syntax in `graphit.resolve()` calls too - the server expands them at query time:
|
|
262
|
+
|
|
263
|
+
```js
|
|
264
|
+
graphit.resolve({
|
|
265
|
+
sql: "SELECT {{dim:INSTALL_MONTH}}, {{metric:ARPU(DAY=7)}} as arpu FROM MARKETING_UA_DS GROUP BY 1",
|
|
266
|
+
dataSourceId: "ds_abc123",
|
|
267
|
+
target: "#arpu-chart"
|
|
268
|
+
});
|
|
269
|
+
```
|
|
243
270
|
|
|
244
271
|
---
|
|
245
272
|
|
|
@@ -259,7 +286,30 @@ const result = await graphit.resolve({
|
|
|
259
286
|
|
|
260
287
|
The `dataSourceId` is the ID from `graphit ds list`. The `target` parameter (CSS selector or element) shows a blur + spinner overlay while loading and removes it on completion. `result.data` is an array of row objects you can render however you want.
|
|
261
288
|
|
|
262
|
-
**Error handling:** `graphit.resolve()` rejects on timeout (
|
|
289
|
+
**Error handling:** `graphit.resolve()` rejects on timeout (120s), bad SQL, or invalid data source ID. Wrap calls in try/catch and show a user-visible error message in the target element on failure. Verify SQL returns data via the CLI before embedding it in HTML.
|
|
290
|
+
|
|
291
|
+
### First-paint loading state
|
|
292
|
+
|
|
293
|
+
The dashboard HTML paints before the SDK connects (iframe load + handshake), so the SDK's own spinner cannot cover the first moments. Bake this pure-CSS overlay into the HTML so every chart shows a spinner from the first frame; the SDK adopts it and removes it when that element's `graphit.resolve()` settles (success or error).
|
|
294
|
+
|
|
295
|
+
Add once to the page `<style>`:
|
|
296
|
+
|
|
297
|
+
```css
|
|
298
|
+
@keyframes gh-spin{to{transform:rotate(360deg)}}
|
|
299
|
+
.gh-loading{position:relative;min-height:120px}
|
|
300
|
+
.gh-loading-overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:9998;backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);background:color-mix(in srgb,var(--graphit-surface-raised,#fff) 50%,transparent);border-radius:inherit}
|
|
301
|
+
.gh-loading-spin{animation:gh-spin .7s linear infinite}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Add inside EVERY element you pass as `target:` to `graphit.resolve()` - and ONLY those elements (a static text or title section with no resolve call would spin forever):
|
|
305
|
+
|
|
306
|
+
```html
|
|
307
|
+
<div id="spend-chart" class="gh-loading">
|
|
308
|
+
<div class="gh-loading-overlay"><svg class="gh-loading-spin" width="24" height="24" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="var(--graphit-border,#e5e5e5)" stroke-width="2.5"/><path d="M12 2a10 10 0 0 1 10 10" stroke="var(--graphit-accent,#4DB6AC)" stroke-width="2.5" stroke-linecap="round"/></svg></div>
|
|
309
|
+
</div>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The class names are a contract with the SDK (`gh-loading`, `gh-loading-overlay`, `gh-loading-spin`, `gh-spin`) - keep them exactly as shown or the SDK cannot adopt and remove the overlay. NEVER write text placeholders ("Loading...", "Fetching data...") - they never animate and make slow loads look stuck.
|
|
263
313
|
|
|
264
314
|
### Rendering
|
|
265
315
|
|
|
@@ -270,7 +320,7 @@ The iframe also provides optional convenience helpers if you want quick standard
|
|
|
270
320
|
| Helper | Usage |
|
|
271
321
|
|---|---|
|
|
272
322
|
| `graphit.chart(el, {type, data, x, y, ...})` | Bar, line, area, donut, scatter, stacked-bar, heatmap, funnel, gauge, sparkline |
|
|
273
|
-
| `graphit.table(el, {data, columns?,
|
|
323
|
+
| `graphit.table(el, {data, columns?, maxRows?})` | Styled HTML table. `columns` is a `string[]` of data keys (NOT objects), defaults to `Object.keys(data[0])` |
|
|
274
324
|
| `graphit.kpi(el, {value, label?, format?})` | KPI card with optional delta |
|
|
275
325
|
|
|
276
326
|
These are shortcuts, not requirements. Use them when a standard chart is all you need. Hand-roll when you want full control over the visualization.
|
|
@@ -284,15 +334,17 @@ These are shortcuts, not requirements. Use them when a standard chart is all you
|
|
|
284
334
|
```html
|
|
285
335
|
<div data-graphit-id="spend-by-source"
|
|
286
336
|
data-graphit-label="Ad Spend by Source"
|
|
287
|
-
data-graphit-kb="metric:CPI,dimension:MEDIA_SOURCE,table:
|
|
288
|
-
data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM
|
|
337
|
+
data-graphit-kb="metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA_DS"
|
|
338
|
+
data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA_DS GROUP BY MEDIA_SOURCE ORDER BY spend DESC"
|
|
289
339
|
data-graphit-ds="ds_abc123">
|
|
290
|
-
<div id="spend-chart"
|
|
340
|
+
<div id="spend-chart" class="gh-loading">
|
|
341
|
+
<div class="gh-loading-overlay"><svg class="gh-loading-spin" width="24" height="24" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="var(--graphit-border,#e5e5e5)" stroke-width="2.5"/><path d="M12 2a10 10 0 0 1 10 10" stroke="var(--graphit-accent,#4DB6AC)" stroke-width="2.5" stroke-linecap="round"/></svg></div>
|
|
342
|
+
</div>
|
|
291
343
|
</div>
|
|
292
344
|
<script>
|
|
293
345
|
(async function() {
|
|
294
346
|
var r = await graphit.resolve({
|
|
295
|
-
sql: "SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM
|
|
347
|
+
sql: "SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA_DS GROUP BY MEDIA_SOURCE ORDER BY spend DESC",
|
|
296
348
|
dataSourceId: "ds_abc123",
|
|
297
349
|
target: "#spend-chart"
|
|
298
350
|
});
|
|
@@ -325,3 +377,4 @@ Detailed knowledge lives in `references/`. Consult the relevant file when you ne
|
|
|
325
377
|
| `domain-lenses.md` | Data matches a business domain. Marketing, finance, product/growth, ops, sales - signals, key metrics, must-have charts, anti-patterns. |
|
|
326
378
|
| `graphit-style.md` | Building the HTML. Design principles, typography scale, color system with usage rules, layout patterns (page structure, KPI cards, data tables). |
|
|
327
379
|
| `chart-patterns.md` | Custom chart implementations. Inline SVG/CSS code for: scatter/bubble, heatmap, funnel, gauge, sparkline, stacked bar, and the shared tooltip pattern. |
|
|
380
|
+
| `governance.md` | Query governance. Reference syntax, trust tiers, enforceable rules, override flow, governance commands. |
|
|
@@ -98,3 +98,8 @@ Before generating the HTML:
|
|
|
98
98
|
- [ ] 3+ chart types for 4+ graphs
|
|
99
99
|
- [ ] Every KPI has definition + baseline + direction
|
|
100
100
|
- [ ] No anti-patterns present
|
|
101
|
+
|
|
102
|
+
## Presenting Dashboard & Connector Results
|
|
103
|
+
|
|
104
|
+
**dashboard list**: table with **bold Name**, ID, URL.
|
|
105
|
+
**connector list**: table with **bold Name**, Account, Auth.
|
|
@@ -62,3 +62,10 @@ Use `kb_explore` with max_depth=2 and no edge_type filter. Returns the table's d
|
|
|
62
62
|
| 3 (max) | Full neighborhood. Use sparingly - can return many results for connected tables. |
|
|
63
63
|
|
|
64
64
|
Start at depth 1. Only increase if the user's question requires it or the initial results are insufficient.
|
|
65
|
+
|
|
66
|
+
## Presenting KB Results
|
|
67
|
+
|
|
68
|
+
**kb list**: `**12 metrics** across 3 tables:` then table with **bold name**, table, calculation. Adapt columns per type.
|
|
69
|
+
**kb get**: `### **CPI** (metric, verified)` + *description* + key-value table (Table, Calculation, Parameters, Topics, Dims).
|
|
70
|
+
**kb search**: `**5 results** for "query":` then table with Type, **bold Name**, Description.
|
|
71
|
+
**kb explore**: tree with bold names indented by level: Asset > Tables > Dimensions > Rules > Domain.
|
|
@@ -117,6 +117,24 @@ SELECT UNNEST(generate_series(
|
|
|
117
117
|
|
|
118
118
|
The outer SELECT can ONLY reference columns the subquery exposes (its aliases). Base-table columns aggregated away in the subquery do NOT exist at the outer level.
|
|
119
119
|
|
|
120
|
+
## Cache-Friendly SQL (Canvas Resolve)
|
|
121
|
+
|
|
122
|
+
Canvas `graphit.resolve()` queries that follow these shapes serve from a semantic cache in ~10ms on filter changes instead of a full DuckDB recompute (5-37s on wide data sources). Write resolve SQL in this style by default.
|
|
123
|
+
|
|
124
|
+
**Shape rules:**
|
|
125
|
+
- Single table (no JOIN/UNION)
|
|
126
|
+
- WHERE as flat AND of `column = literal`, `column IN (...)`, `column BETWEEN ... AND ...` conjuncts
|
|
127
|
+
- Bare aggregates only: `SUM(col)`, `COUNT(*)`, `MIN(col)`, `MAX(col)` - no wrapping functions (`ROUND(SUM(x))`), no aggregate arithmetic (`SUM(a)/NULLIF(SUM(b),0)`), no `AVG` (v2)
|
|
128
|
+
- Literal dates (`>= '2026-01-01'`), never `CURRENT_DATE` expressions
|
|
129
|
+
- GROUP BY column names or ordinals; ORDER BY / LIMIT allowed (outer only)
|
|
130
|
+
- CTEs are fine when the CTE body follows the same rules
|
|
131
|
+
|
|
132
|
+
**Shapes that skip the cache** (fall back to normal execution, still correct):
|
|
133
|
+
- `COUNT(DISTINCT x)`, window functions, HAVING, QUALIFY
|
|
134
|
+
- OR / NOT in WHERE
|
|
135
|
+
- Ratio metrics (`SUM(a)/NULLIF(SUM(b),0)`) - compute client-side or use two resolves
|
|
136
|
+
- `CURRENT_DATE`-relative predicates
|
|
137
|
+
|
|
120
138
|
## Data Source Routing
|
|
121
139
|
|
|
122
140
|
| Situation | Command | Speed |
|
|
@@ -125,3 +143,18 @@ The outer SELECT can ONLY reference columns the subquery exposes (its aliases).
|
|
|
125
143
|
| No data source | `graphit query "SQL" --warehouse --connection <id>` | ~10s, Snowflake |
|
|
126
144
|
|
|
127
145
|
Always prefer cached data sources. Check with `graphit ds list`. If no data source covers the table, suggest creating one for future speed.
|
|
146
|
+
|
|
147
|
+
## Presenting Query Results
|
|
148
|
+
|
|
149
|
+
Ground every query in the KB. When using `{{metric:X}}`/`{{dim:X}}` references, show five sections:
|
|
150
|
+
1. **KB Assets:** list all referenced metrics, dimensions, tables as bold names
|
|
151
|
+
2. **Query:** original SQL with `{{metric:X}}` references in ```sql block
|
|
152
|
+
3. **Resolved SQL:** expanded SQL (always use `--verbose`) in ```sql block
|
|
153
|
+
4. **Results:** markdown table with right-aligned numbers, row count, DS ID
|
|
154
|
+
5. **Governance:** tier, KB refs count, rules enforced (**bold names**), max rows
|
|
155
|
+
|
|
156
|
+
For inline SQL (no KB refs): show query + results + governance, and suggest the KB reference equivalent to nudge toward governed tier.
|
|
157
|
+
|
|
158
|
+
## Presenting Data Source Results
|
|
159
|
+
|
|
160
|
+
After `graphit ds list`: table with **bold Name**, ID, Rows (right-aligned), Status, Governed. End with DS recommendation for current task.
|
|
@@ -79,6 +79,12 @@ Use the system font stack everywhere: `-apple-system, BlinkMacSystemFont, 'Segoe
|
|
|
79
79
|
box-shadow:0 1px 3px rgba(0,0,0,0.08); }
|
|
80
80
|
.card h3 { font-size:12px; font-weight:600; color:var(--graphit-fg-subtle); text-transform:uppercase;
|
|
81
81
|
letter-spacing:0.05em; margin-bottom:16px; }
|
|
82
|
+
@keyframes gh-spin{to{transform:rotate(360deg)}}
|
|
83
|
+
.gh-loading { position:relative; min-height:120px; }
|
|
84
|
+
.gh-loading-overlay { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
|
|
85
|
+
z-index:9998; backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px);
|
|
86
|
+
background:color-mix(in srgb,var(--graphit-surface-raised,#fff) 50%,transparent); border-radius:inherit; }
|
|
87
|
+
.gh-loading-spin { animation:gh-spin .7s linear infinite; }
|
|
82
88
|
@media(max-width:900px) {
|
|
83
89
|
.kpi-grid { grid-template-columns:repeat(2,1fr); }
|
|
84
90
|
.charts-grid { grid-template-columns:1fr; }
|
|
@@ -118,6 +124,16 @@ Use the system font stack everywhere: `-apple-system, BlinkMacSystemFont, 'Segoe
|
|
|
118
124
|
</style>
|
|
119
125
|
```
|
|
120
126
|
|
|
127
|
+
### Loading State
|
|
121
128
|
|
|
129
|
+
Bake this overlay inside every element passed as `target:` to `graphit.resolve()` - and only those (static text/title cards would spin forever). It shows from the first paint, before the SDK connects; the SDK removes it when the resolve settles. Class names are the SDK contract - keep them exactly:
|
|
130
|
+
|
|
131
|
+
```html
|
|
132
|
+
<div id="spend-chart" class="gh-loading">
|
|
133
|
+
<div class="gh-loading-overlay"><svg class="gh-loading-spin" width="24" height="24" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="var(--graphit-border,#e5e5e5)" stroke-width="2.5"/><path d="M12 2a10 10 0 0 1 10 10" stroke="var(--graphit-accent,#4DB6AC)" stroke-width="2.5" stroke-linecap="round"/></svg></div>
|
|
134
|
+
</div>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Never write "Loading..." text placeholders - they don't animate and make slow loads look stuck.
|
|
122
138
|
|
|
123
139
|
For inline chart implementations (bar, line, donut, heatmap, funnel, sparkline, gauge, stacked bar, multi-series), see `chart-patterns.md`.
|