@graphit/cli 0.1.1 → 0.1.2

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.
Files changed (53) hide show
  1. package/package.json +1 -1
  2. package/skill/SKILL.md +68 -23
  3. package/skill/cursor/graphit-chart-patterns.mdc +67 -129
  4. package/skill/graphit.mdc +62 -80
  5. package/skill/references/chart-patterns.md +41 -218
  6. package/dist/api/client.d.ts +0 -16
  7. package/dist/api/client.js +0 -67
  8. package/dist/api/client.js.map +0 -1
  9. package/dist/auth/credentials.d.ts +0 -15
  10. package/dist/auth/credentials.js +0 -34
  11. package/dist/auth/credentials.js.map +0 -1
  12. package/dist/auth/login.d.ts +0 -2
  13. package/dist/auth/login.js +0 -230
  14. package/dist/auth/login.js.map +0 -1
  15. package/dist/auth/token.d.ts +0 -5
  16. package/dist/auth/token.js +0 -42
  17. package/dist/auth/token.js.map +0 -1
  18. package/dist/commands/auth.d.ts +0 -2
  19. package/dist/commands/auth.js +0 -68
  20. package/dist/commands/auth.js.map +0 -1
  21. package/dist/commands/connector.d.ts +0 -2
  22. package/dist/commands/connector.js +0 -97
  23. package/dist/commands/connector.js.map +0 -1
  24. package/dist/commands/dashboard.d.ts +0 -2
  25. package/dist/commands/dashboard.js +0 -124
  26. package/dist/commands/dashboard.js.map +0 -1
  27. package/dist/commands/ds.d.ts +0 -2
  28. package/dist/commands/ds.js +0 -53
  29. package/dist/commands/ds.js.map +0 -1
  30. package/dist/commands/kb.d.ts +0 -2
  31. package/dist/commands/kb.js +0 -259
  32. package/dist/commands/kb.js.map +0 -1
  33. package/dist/commands/query.d.ts +0 -2
  34. package/dist/commands/query.js +0 -61
  35. package/dist/commands/query.js.map +0 -1
  36. package/dist/commands/setup.d.ts +0 -2
  37. package/dist/commands/setup.js +0 -173
  38. package/dist/commands/setup.js.map +0 -1
  39. package/dist/config.d.ts +0 -4
  40. package/dist/config.js +0 -9
  41. package/dist/config.js.map +0 -1
  42. package/dist/index.d.ts +0 -2
  43. package/dist/index.js +0 -24
  44. package/dist/index.js.map +0 -1
  45. package/dist/output/format.d.ts +0 -8
  46. package/dist/output/format.js +0 -30
  47. package/dist/output/format.js.map +0 -1
  48. package/dist/output/json.d.ts +0 -1
  49. package/dist/output/json.js +0 -4
  50. package/dist/output/json.js.map +0 -1
  51. package/dist/output/table.d.ts +0 -2
  52. package/dist/output/table.js +0 -17
  53. package/dist/output/table.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphit/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Graphit CLI - Build custom dashboards from any AI coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -4,7 +4,8 @@ description: >
4
4
  Build HTML dashboards with Graphit. KB-aware queries, entity wrapping, cached data sources.
5
5
  Triggers on: "dashboard", "graphit", "KB", "metric", "data source", "build a dashboard",
6
6
  "explore the KB", "query data", "custom dashboard".
7
- version: "0.4.0"
7
+ Do NOT activate for: editing platform-native dashboard widgets, non-HTML output formats,
8
+ general data analysis without a dashboard deliverable.
8
9
  ---
9
10
 
10
11
  # Graphit CLI
@@ -21,8 +22,7 @@ Your HTML must NEVER contain:
21
22
  - `<img src="https://...">` - no external images
22
23
 
23
24
  If you use ANY `src=` or `href=` pointing to a URL, the dashboard will be blank.
24
- Build charts with: CSS width% bars or inline SVG (preferred - crisp at any DPI in the iframe).
25
- All CSS in `<style>`, all JS in `<script>`, all fonts from system stack.
25
+ All CSS in `<style>`, all JS in `<script>`, all fonts from system stack. The iframe has a built-in runtime (`graphit.chart`, `graphit.table`, `graphit.kpi`) for standard visualizations - use it instead of importing libraries.
26
26
 
27
27
  ### 2. ALWAYS query through data sources
28
28
  NEVER query the warehouse directly when a cached data source covers the table. Data sources return in ~100ms. Warehouse queries take ~10s and cost Snowflake credits.
@@ -59,15 +59,21 @@ Missing any attribute = broken entity. Missing wrapping entirely = invisible to
59
59
 
60
60
  **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.
61
61
 
62
+ ### 4. ALWAYS use graphit.resolve() for live data
63
+ 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.
64
+
65
+ **Wrong:** Running queries at build time, embedding results as `const data = [{...}, ...]` in the HTML.
66
+ **Right:** Using `graphit.resolve({sql, dataSourceId})` in a `<script>` block so data refreshes automatically.
67
+
62
68
  ---
63
69
 
64
70
  ## Workflow
65
71
 
66
72
  1. **Ask the user** what dashboard they want. Don't start querying until you know what they need.
67
- 2. **Explore KB** to understand available metrics, dimensions, tables, and tables.
73
+ 2. **Explore KB** to understand available metrics, dimensions, tables, and rules.
68
74
  3. **Find a data source** (`graphit ds list`) - prefer cached data sources (~100ms) over live warehouse (~10s).
69
- 4. **Query data** and embed results as inline JS variables.
70
- 5. **Build HTML** - all CSS and JS must be inline. Write to a local `.html` file.
75
+ 4. **Query data to validate** - run queries via the CLI to verify SQL and preview results. Show the user what you found.
76
+ 5. **Build HTML** - write `graphit.resolve()` calls for live data + `graphit.chart/table/kpi` for rendering. All CSS in `<style>`, all JS in `<script>`. Write to a local `.html` file.
71
77
  6. **Save** with `graphit dashboard update-html <id> --file <path>`.
72
78
  7. Give the user the dashboard URL so they can open it.
73
79
 
@@ -130,26 +136,65 @@ Found **12 metrics** and **8 dimensions** on table MARKETING_UA:
130
136
 
131
137
  ---
132
138
 
133
- ## Chart Type Quick Reference
139
+ ## graphit.resolve() - Live Data API
140
+
141
+ The iframe provides `graphit.resolve()` to fetch live data from cached data sources. This is how your HTML gets its data - never embed static query results.
142
+
143
+ ```js
144
+ const result = await graphit.resolve({
145
+ sql: "SELECT region, SUM(revenue) as rev FROM orders GROUP BY region",
146
+ dataSourceId: "ds_abc123",
147
+ target: "#chart-container", // optional: shows loading spinner on element
148
+ maxRows: 10000 // optional: default 10K, max 10K
149
+ });
150
+ // Returns: { columns: string[], data: object[], rowCount: number, truncated: boolean }
151
+ ```
152
+
153
+ 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.
154
+
155
+ **Error handling:** `graphit.resolve()` rejects on timeout (60s), 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.
156
+
157
+ ### Rendering
134
158
 
135
- **NEVER use `<canvas>`.** The dashboard renders in a sandboxed iframe where canvas `devicePixelRatio` handling breaks, producing blurry charts. Use SVG (`viewBox` scales perfectly at any DPI) or CSS-based techniques instead.
159
+ You have full creative freedom for how to present data. Build charts with inline SVG, CSS, HTML tables, creative layouts - whatever fits the dashboard best.
136
160
 
137
- | Data shape | Chart | Inline technique |
138
- |---|---|---|
139
- | 1 temporal + 1 numeric | Line | SVG `<polyline>` with `viewBox` |
140
- | 1 categorical + 1 numeric | Bar | CSS width% divs |
141
- | Single number | KPI card | Styled div |
142
- | Part-whole (max 5) | Donut | SVG arc paths |
143
- | 2 numerics + optional size | Scatter / Bubble | SVG `<circle>` elements |
144
- | Matrix / cohort | Heatmap | CSS grid + opacity |
145
- | Stages | Funnel | CSS width% bars, narrowing |
146
- | Time sparkline | Sparkline | Inline SVG `<polyline>` |
147
- | Detail / raw data | Table | HTML table + hover rows |
161
+ The iframe also provides optional convenience helpers if you want quick standard charts:
148
162
 
149
- **SVG tooltip pattern:** Add a shared tooltip `<div>` (position:fixed, pointer-events:none). Attach `mouseenter`/`mouseleave` on SVG `<g>` groups wrapping each data element. For line charts, overlay invisible `<rect>` columns per x-tick as hover targets, with a dashed vertical guide `<line>` toggled on hover.
163
+ | Helper | Usage |
164
+ |---|---|
165
+ | `graphit.chart(el, {type, data, x, y, ...})` | Bar, line, area, donut, scatter, stacked-bar, heatmap, funnel, gauge, sparkline |
166
+ | `graphit.table(el, {data, columns?, ...})` | Styled HTML table |
167
+ | `graphit.kpi(el, {value, label?, format?})` | KPI card with optional delta |
150
168
 
151
- For more chart types (scatter, stacked bar, gauge, multi-series) see `references/chart-selection.md`.
152
- For inline implementations of each, see `references/chart-patterns.md`.
169
+ 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.
170
+
171
+ `graphit.chart` types: `"bar"`, `"line"`, `"area"`, `"donut"` (alias `"pie"`), `"scatter"` (alias `"bubble"`), `"stacked-bar"` (alias `"stacked"`), `"heatmap"`, `"funnel"`, `"gauge"`, `"sparkline"`. Config: `x` (category field), `y` (value field), `series` (group-by field), `title`, `height` (140-900px), `valueFormat` (`"currency"` | `"percent"` | `"number"`), `colors` (array). Scatter adds: `size` (bubble radius field), `label` (tooltip field). Gauge adds: `min`, `max`, `format`. Sparkline adds: `width`, `showValue`.
172
+
173
+ `graphit.kpi` config: `value`, `label`, `format` (`"currency"` | `"percent"` | `"number"`), `compareValue`, `compareLabel`.
174
+
175
+ ### Canonical pattern - entity with live data
176
+
177
+ ```html
178
+ <div data-graphit-id="spend-by-source"
179
+ data-graphit-label="Ad Spend by Source"
180
+ data-graphit-kb="metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA"
181
+ data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA GROUP BY MEDIA_SOURCE ORDER BY spend DESC">
182
+ <div id="spend-chart"></div>
183
+ </div>
184
+ <script>
185
+ (async function() {
186
+ var r = await graphit.resolve({
187
+ sql: "SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA GROUP BY MEDIA_SOURCE ORDER BY spend DESC",
188
+ dataSourceId: "ds_abc123",
189
+ target: "#spend-chart"
190
+ });
191
+ graphit.chart("#spend-chart", {
192
+ type: "bar", data: r.data, x: "MEDIA_SOURCE", y: "spend",
193
+ title: "Ad Spend by Source", valueFormat: "currency"
194
+ });
195
+ })();
196
+ </script>
197
+ ```
153
198
 
154
199
  ---
155
200
 
@@ -165,4 +210,4 @@ Detailed knowledge lives in `references/`. Consult the relevant file when you ne
165
210
  | `sql-reference.md` | Writing queries. DuckDB/Snowflake translation, formatting standards, gap-filling, JSON access, data source routing. |
166
211
  | `domain-lenses.md` | Data matches a business domain. Marketing, finance, product/growth, ops, sales - signals, key metrics, must-have charts, anti-patterns. |
167
212
  | `graphit-style.md` | Building the HTML. Design principles, typography scale, color system with usage rules, layout patterns (page structure, KPI cards, data tables). |
168
- | `chart-patterns.md` | Implementing charts. Inline CSS/SVG/canvas code for: horizontal bar, vertical bar, stacked bar, line/area, multi-series line, sparkline, donut, heatmap grid, funnel, gauge. |
213
+ | `chart-patterns.md` | Custom chart implementations. Inline SVG/CSS code for: scatter/bubble, heatmap, funnel, gauge, sparkline, stacked bar, and the shared tooltip pattern. |
@@ -1,148 +1,86 @@
1
1
  ---
2
2
  alwaysApply: false
3
- description: "Graphit: Inline CSS/SVG/canvas implementations for dashboards. No external libraries - the iframe CSP blocks all external resources."
3
+ description: "Graphit: Runtime chart types via graphit.chart() and hand-rolled patterns. All data from graphit.resolve()."
4
4
  globs: []
5
5
  ---
6
6
 
7
7
  # Chart Patterns
8
8
 
9
- Inline CSS/SVG/canvas implementations for dashboards. No external libraries - the iframe CSP blocks all external resources.
9
+ All chart types are available via `graphit.chart()`. All data comes from `graphit.resolve()` - never embed static data.
10
10
 
11
- ## CSS Horizontal Bar
12
- ```js
13
- var maxVal = Math.max.apply(null, data.map(function(d){return d.value}));
14
- data.forEach(function(d) {
15
- var pct = (d.value / maxVal * 100).toFixed(1);
16
- container.innerHTML += '<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">'
17
- + '<span style="width:100px;font-size:13px;font-weight:500;text-align:right">' + d.name + '</span>'
18
- + '<div style="flex:1;height:28px;background:var(--graphit-surface-sunken);border-radius:6px;overflow:hidden">'
19
- + '<div style="width:' + pct + '%;height:100%;background:var(--graphit-accent);border-radius:6px;min-width:2px"></div></div>'
20
- + '<span style="width:70px;font-size:13px;font-weight:600;text-align:right">' + fmt(d.value) + '</span></div>';
21
- });
22
- ```
11
+ **NEVER use `<canvas>`.** Canvas produces blurry charts inside the sandboxed iframe due to DPI scaling issues.
23
12
 
24
- ## CSS Vertical Bar (Column)
25
- ```js
26
- var maxVal = Math.max.apply(null, data.map(function(d){return d.value}));
27
- container.style.cssText = 'display:flex;align-items:flex-end;gap:8px;height:200px;padding-top:20px';
28
- data.forEach(function(d) {
29
- var pct = (d.value / maxVal * 100).toFixed(1);
30
- container.innerHTML += '<div style="flex:1;height:' + pct + '%;background:var(--graphit-accent);border-radius:4px 4px 0 0;'
31
- + 'position:relative;min-width:30px">'
32
- + '<span style="position:absolute;top:-20px;left:50%;transform:translateX(-50%);font-size:12px;font-weight:600">'
33
- + d.value + '</span>'
34
- + '<span style="position:absolute;bottom:-22px;left:50%;transform:translateX(-50%);font-size:11px;color:var(--graphit-fg-subtle);white-space:nowrap">'
35
- + d.name + '</span></div>';
36
- });
37
- ```
13
+ ## Runtime chart types
38
14
 
39
- ## CSS Stacked Bar
40
- ```js
41
- var colors = ['var(--graphit-accent)','#80CBC4','#26A69A','#5FA39B'];
42
- data.forEach(function(row) {
43
- var total = row.segments.reduce(function(a,s){return a+s.value},0);
44
- var html = '<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">'
45
- + '<span style="width:100px;font-size:13px;font-weight:500;text-align:right">' + row.name + '</span>'
46
- + '<div style="flex:1;height:28px;display:flex;border-radius:6px;overflow:hidden">';
47
- row.segments.forEach(function(s, i) {
48
- html += '<div style="width:' + (s.value/total*100).toFixed(1) + '%;height:100%;background:' + colors[i%colors.length] + '"></div>';
49
- });
50
- html += '</div><span style="width:70px;font-size:13px;font-weight:600;text-align:right">' + total + '</span></div>';
51
- container.innerHTML += html;
52
- });
53
- ```
15
+ ### bar
16
+ SVG vertical bars. Multi-series: grouped side-by-side. Y-axis: 4 grid lines with formatted labels. X-axis: category labels (sampled if >12). Rounded top corners (rx=3). `<title>` tooltips per bar.
54
17
 
55
- ## SVG Line/Area Chart
56
- ```js
57
- var w = 600, h = 200, pad = 20;
58
- var maxVal = Math.max.apply(null, data.map(function(d){return d.value}));
59
- var points = data.map(function(d, i) {
60
- var x = pad + (i / (data.length - 1)) * (w - 2*pad);
61
- var y = h - pad - ((d.value / maxVal) * (h - 2*pad));
62
- return x + ',' + y;
63
- });
64
- svg.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
65
- svg.innerHTML =
66
- '<path d="M' + points.join(' L') + ' L' + (w-pad) + ',' + (h-pad) + ' L' + pad + ',' + (h-pad) + ' Z" fill="rgba(77,182,172,0.15)"/>'
67
- + '<path d="M' + points.join(' L') + '" fill="none" stroke="var(--graphit-accent)" stroke-width="2"/>';
68
- ```
18
+ ### line / area
19
+ SVG line with circle dots at each point. Area adds filled path below (opacity 0.18). Multi-series: separate colored paths. Y-axis scaled to data range (min to max). `<title>` tooltips per dot.
69
20
 
70
- ## SVG Multi-Series Line
71
- ```js
72
- var colors = ['var(--graphit-accent)','#FF7043','#7E57C2','#42A5F5'];
73
- series.forEach(function(s, si) {
74
- var pts = s.values.map(function(v, i) {
75
- var x = pad + (i / (s.values.length - 1)) * (w - 2*pad);
76
- var y = h - pad - ((v / maxAll) * (h - 2*pad));
77
- return x + ',' + y;
78
- });
79
- svg.innerHTML += '<path d="M' + pts.join(' L') + '" fill="none" stroke="' + colors[si] + '" stroke-width="2"/>';
80
- });
81
- ```
21
+ ### donut / pie
22
+ SVG pie slices with center hole (58% of radius). Center shows formatted total. Legend below with color swatches.
82
23
 
83
- ## SVG Sparkline (for KPI cards)
84
- ```js
85
- var vals = [10,15,12,18,22,19,25];
86
- var max = Math.max.apply(null,vals), min = Math.min.apply(null,vals);
87
- var pts = vals.map(function(v,i){
88
- return (i/(vals.length-1))*120 + ',' + (30 - ((v-min)/(max-min||1))*26 - 2);
89
- }).join(' L');
90
- sparkSvg.innerHTML = '<path d="M' + pts + '" fill="none" stroke="var(--graphit-accent)" stroke-width="1.5"/>';
91
- ```
24
+ ### scatter / bubble
25
+ SVG circles positioned by x/y. Size field scales radius (sqrt, 3-40px). Fill opacity 0.6 with solid stroke. 4 horizontal grid lines. Capped at 200 points.
26
+ Config: `x`, `y`, `size` (optional radius), `label` (tooltip), `xFormat`.
92
27
 
93
- ## Canvas Donut Chart
94
- ```js
95
- var ctx = canvas.getContext('2d');
96
- var slices = [{v:40,c:'var(--graphit-accent)'},{v:25,c:'#80CBC4'},{v:20,c:'#26A69A'},{v:15,c:'#009688'}];
97
- var total = slices.reduce(function(a,s){return a+s.v},0);
98
- var cx=150, cy=150, r=120, hole=70, angle = -Math.PI/2;
99
- slices.forEach(function(s) {
100
- var arc = (s.v/total)*Math.PI*2;
101
- ctx.beginPath(); ctx.moveTo(cx,cy);
102
- ctx.arc(cx,cy,r,angle,angle+arc);
103
- ctx.fillStyle=s.c; ctx.fill(); angle+=arc;
104
- });
105
- ctx.beginPath(); ctx.arc(cx,cy,hole,0,Math.PI*2);
106
- ctx.fillStyle='white'; ctx.fill();
107
- ```
28
+ ### stacked-bar / stacked
29
+ SVG vertical stacked bars. Segments stacked bottom-to-top per category. Y-axis scaled to max total. Legend shows series names.
30
+ Config: requires `series` field for stacking groups.
108
31
 
109
- ## CSS Heatmap Grid
110
- ```js
111
- var max = Math.max.apply(null, cells.map(function(c){return c.value}));
112
- container.style.cssText = 'display:grid;grid-template-columns:repeat(' + cols + ',1fr);gap:2px';
113
- cells.forEach(function(c) {
114
- var intensity = c.value / max;
115
- var bg = 'rgba(77,182,172,' + (0.1 + intensity*0.9).toFixed(2) + ')';
116
- var fg = intensity > 0.6 ? 'white' : 'var(--graphit-fg)';
117
- container.innerHTML += '<div style="aspect-ratio:1;border-radius:3px;display:flex;align-items:center;'
118
- + 'justify-content:center;font-size:11px;font-weight:600;background:' + bg + ';color:' + fg + '">'
119
- + c.value + '</div>';
120
- });
121
- ```
32
+ ### heatmap
33
+ CSS grid. X = columns, Y = rows. Background intensity scales linearly with theme accent token. Text flips to raised surface color at >55% intensity. Headers auto-generated. Capped at 40x40.
34
+ Config: `x` (column), `y` (row), `value` (intensity, falls back to `y`).
122
35
 
123
- ## CSS Funnel
124
- ```js
125
- var maxVal = data[0].value;
126
- data.forEach(function(d, i) {
127
- var pct = (d.value / maxVal * 100).toFixed(1);
128
- var rate = i > 0 ? (d.value / data[i-1].value * 100).toFixed(1) + '%' : '';
129
- container.innerHTML += '<div style="display:flex;align-items:center;gap:12px;margin-bottom:6px">'
130
- + '<span style="width:120px;font-size:13px;text-align:right">' + d.name + '</span>'
131
- + '<div style="width:' + pct + '%;height:36px;background:var(--graphit-accent);border-radius:4px;'
132
- + 'display:flex;align-items:center;padding:0 12px;color:white;font-weight:600;font-size:13px">'
133
- + d.value.toLocaleString() + '</div>'
134
- + '<span style="font-size:12px;color:var(--graphit-fg-subtle)">' + rate + '</span></div>';
135
- });
136
- ```
36
+ ### funnel
37
+ CSS horizontal bars narrowing top-to-bottom. First bar = 100% width, rest proportional. Shows step-over-step conversion rate. Colors cycle through palette.
38
+
39
+ ### gauge
40
+ CSS half-circle via `conic-gradient` + `clip-path`. Auto-colors: green (>=70%), yellow (>=40%), red (<40%). Override with `color` config (must be a valid CSS color token).
41
+ Config: `value`, `min` (default 0), `max` (default 100), `format`, `label`, `color`.
42
+
43
+ ### sparkline
44
+ Inline SVG polyline for KPI cards or table cells. No axes, no grid - just trend shape. Single data point renders as a dot.
45
+ Config: `y`, `width` (default 120), `height` (default 32), `label`, `showValue` (default true), `valueFormat`.
46
+
47
+ ## Color tokens
48
+
49
+ | Token | Usage |
50
+ |---|---|
51
+ | `var(--graphit-accent)` | Primary brand teal |
52
+ | `var(--graphit-success)` | Positive/good |
53
+ | `var(--graphit-warning)` | Caution |
54
+ | `var(--graphit-error)` | Negative/bad |
55
+ | `var(--graphit-fg)` | Primary text |
56
+ | `var(--graphit-fg-muted)` | Secondary text |
57
+ | `var(--graphit-fg-subtle)` | Labels, placeholders |
58
+ | `var(--graphit-border)` | Borders, grid lines |
59
+ | `var(--graphit-surface-raised)` | Card backgrounds |
60
+ | `var(--graphit-surface-sunken)` | Inset areas |
61
+
62
+ ## Tooltip pattern (for hand-rolled charts)
63
+
64
+ Runtime charts use `<title>` elements for native browser tooltips. For hand-rolled charts, add ONE shared tooltip div and always escape user data before passing to `showTooltip()`:
137
65
 
138
- ## CSS Gauge / Progress
139
66
  ```html
140
- <div style="position:relative;width:200px;height:100px;overflow:hidden">
141
- <div id="gauge-arc" style="width:200px;height:200px;border-radius:50%;
142
- background:conic-gradient(var(--graphit-accent) 0% 72%, var(--graphit-surface-sunken) 72% 100%);
143
- clip-path:inset(0 0 50% 0)"></div>
144
- <div style="position:absolute;bottom:0;left:50%;transform:translateX(-50%);
145
- font-size:28px;font-weight:700">72%</div>
146
- </div>
67
+ <div id="tooltip" style="position:fixed;pointer-events:none;z-index:100;
68
+ background:var(--graphit-surface-raised);color:var(--graphit-fg);
69
+ border:1px solid var(--graphit-border);border-radius:8px;
70
+ padding:10px 14px;font-size:13px;line-height:1.5;
71
+ box-shadow:0 4px 12px rgba(0,0,0,0.15);opacity:0;transition:opacity 0.15s;
72
+ max-width:240px"></div>
73
+ ```
74
+
75
+ ```js
76
+ var tooltip = document.getElementById('tooltip');
77
+ function showTooltip(e, html) {
78
+ tooltip.innerHTML = html;
79
+ tooltip.style.opacity = '1';
80
+ var tx = Math.min(e.clientX + 12, window.innerWidth - 260);
81
+ var ty = e.clientY - tooltip.offsetHeight - 8;
82
+ tooltip.style.left = tx + 'px';
83
+ tooltip.style.top = (ty < 4 ? e.clientY + 16 : ty) + 'px';
84
+ }
85
+ function hideTooltip() { tooltip.style.opacity = '0'; }
147
86
  ```
148
- Dynamic: set `background: conic-gradient(var(--graphit-accent) 0% ${pct}%, var(--graphit-surface-sunken) ${pct}% 100%)`.
package/skill/graphit.mdc CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  alwaysApply: false
3
- description: "Build Graphit HTML dashboards with KB-aware queries and entity wrapping"
3
+ description: "Build Graphit HTML dashboards with KB-aware queries, entity wrapping, and cached data sources"
4
4
  globs: []
5
5
  ---
6
6
 
@@ -18,11 +18,15 @@ Your HTML must NEVER contain:
18
18
  - `<img src="https://...">` - no external images
19
19
 
20
20
  If you use ANY `src=` or `href=` pointing to a URL, the dashboard will be blank.
21
- Build charts with: CSS width% bars, inline SVG paths, or `<canvas>` with inline drawing code.
22
- All CSS in `<style>`, all JS in `<script>`, all fonts from system stack.
21
+ All CSS in `<style>`, all JS in `<script>`, all fonts from system stack. The iframe has a built-in runtime (`graphit.chart`, `graphit.table`, `graphit.kpi`) for standard visualizations - use it instead of importing libraries.
23
22
 
24
- ### 2. EVERY element must have entity wrapping
25
- 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 FOUR attributes:
23
+ ### 2. ALWAYS query through data sources
24
+ NEVER query the warehouse directly when a cached data source covers the table. Data sources return in ~100ms. Warehouse queries take ~10s and cost Snowflake credits.
25
+
26
+ Run `graphit ds list` FIRST. If a DS covers your table, use `graphit query "SQL" --ds <id>`. Only use `--warehouse` if NO data source exists and the user approves.
27
+
28
+ ### 3. EVERY element must have entity wrapping
29
+ Without `data-graphit-*` attributes, elements are invisible to the platform. Every chart, KPI card, table, and text section needs ALL FOUR attributes:
26
30
 
27
31
  ```html
28
32
  <div data-graphit-id="revenue-trend"
@@ -42,94 +46,72 @@ Without `data-graphit-*` attributes, elements are invisible to the platform - no
42
46
 
43
47
  KB types: `metric`, `dimension`, `table`, `rule`. Names are UPPER_SNAKE_CASE matching the KB exactly.
44
48
 
45
- **Label = visible title.** The `data-graphit-label` MUST match the card's visible heading exactly. Users see labels in @ mention dropdowns - mismatched labels mean they can't find their chart.
49
+ **Label = visible title.** The `data-graphit-label` MUST match the card's visible heading exactly.
46
50
 
47
- ### 2. ALWAYS query through data sources
48
- NEVER query the warehouse directly when a cached data source covers the table. Data sources return in ~100ms. Warehouse queries take ~10s and cost Snowflake credits.
51
+ ### 4. ALWAYS use graphit.resolve() for live data
52
+ 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.
49
53
 
50
- Run `graphit ds list` FIRST. If a DS covers your table, use `graphit query "SQL" --ds <id>`. Only use `--warehouse` if NO data source exists and the user approves.
54
+ **Wrong:** Embedding results as `const data = [{...}, ...]` in the HTML.
55
+ **Right:** Using `graphit.resolve({sql, dataSourceId})` in a `<script>` block so data refreshes automatically.
51
56
 
52
57
  ---
53
58
 
54
59
  ## Workflow
55
60
 
56
- 1. **Ask the user** what dashboard they want. Don't start querying until you know what they need.
57
- 2. **Explore KB** to understand available metrics, dimensions, tables, and tables.
58
- 3. **Find a data source** (`graphit ds list`) - prefer cached data sources (~100ms) over live warehouse (~10s).
59
- 4. **Query data** and embed results as inline JS variables.
60
- 5. **Build HTML** - all CSS and JS must be inline. Write to a local `.html` file.
61
+ 1. **Ask the user** what dashboard they want.
62
+ 2. **Explore KB** to understand available metrics, dimensions, tables, and rules.
63
+ 3. **Find a data source** (`graphit ds list`) - prefer cached data sources over live warehouse.
64
+ 4. **Query data to validate** - run queries via the CLI to verify SQL and preview results.
65
+ 5. **Build HTML** - write `graphit.resolve()` calls for live data + `graphit.chart/table/kpi` for rendering. All CSS in `<style>`, all JS in `<script>`.
61
66
  6. **Save** with `graphit dashboard update-html <id> --file <path>`.
62
- 7. Give the user the dashboard URL so they can open it.
63
-
64
- ## Commands
65
-
66
- | Command | Description |
67
- |---------|-------------|
68
- | `graphit kb list <type>` | List metrics, dimensions, tables, rules, domains, synonyms |
69
- | `graphit kb get <type> <name>` | Full entity details by name |
70
- | `graphit kb search <query>` | Search across all KB types |
71
- | `graphit kb explore metric <name>` | Metric -> tables -> dimensions graph |
72
- | `graphit ds list` | List cached data sources (use these for fast queries) |
73
- | `graphit query "<sql>" --ds <id>` | Query cached data source (~100ms) |
74
- | `graphit query "<sql>" --warehouse --connection <id>` | Query live Snowflake (~10s) |
75
- | `graphit dashboard create --name "..."` | Create dashboard (returns ID) |
76
- | `graphit dashboard update-html <id> --file <path>` | Upload HTML to dashboard |
77
- | `graphit dashboard list` | List existing dashboards |
78
- | `graphit metadata schemas --connection <id>` | List Snowflake schemas |
79
- | `graphit connector list` | List active connections |
80
-
81
- ## Presenting Results to the User
82
-
83
- The user CANNOT see raw CLI output clearly. You MUST format and present every result - never silently consume tool output and move on.
84
-
85
- **After every query**, show the data:
67
+ 7. Give the user the dashboard URL.
68
+
69
+ ## graphit.resolve() - Live Data API
70
+
71
+ ```js
72
+ const result = await graphit.resolve({
73
+ sql: "SELECT region, SUM(revenue) as rev FROM orders GROUP BY region",
74
+ dataSourceId: "ds_abc123",
75
+ target: "#chart-container", // optional: shows loading spinner
76
+ maxRows: 10000 // optional: default 10K, max 10K
77
+ });
78
+ // Returns: { columns: string[], data: object[], rowCount: number, truncated: boolean }
86
79
  ```
87
- Queried **MARKETING_UA** (ds_abc123, 6 rows):
88
80
 
89
- | Channel | Spend | Installs | CPI |
90
- |-------------|----------|----------|-------|
91
- | Facebook | $42,100 | 12,400 | $3.40 |
92
- | Google UAC | $38,500 | 9,800 | $3.93 |
93
- | TikTok | $21,300 | 8,200 | $2.60 |
94
- ```
95
- ```
81
+ **Error handling:** Rejects on timeout (60s), bad SQL, or invalid data source ID. Wrap in try/catch.
96
82
 
97
- **After KB exploration**, summarize what you found:
98
- ```
99
- Found **12 metrics** and **8 dimensions** on table MARKETING_UA:
100
- - **Metrics:** TOTAL_SPEND, CPI, ROAS_D7, ROAS_D30, INSTALLS, ...
101
- - **Dimensions:** MEDIA_SOURCE, CAMPAIGN_NAME, COUNTRY, PLATFORM, ...
102
- - **Rules:** EXCLUDE_ORGANIC (filters organic installs from paid metrics)
103
- ```
83
+ ### Runtime chart helpers
104
84
 
105
- **Formatting rules:**
106
- - **Bold** metric names, table names, and key numbers
107
- - Use **markdown tables** for any tabular data (query results, entity lists, comparisons)
108
- - Format numbers: commas for thousands (`12,400`), `$` for currency, `%` for rates
109
- - After KB or data source discovery, list what's available before asking what to build
110
- - Show the SQL you ran (in a code block) so the user can validate the logic
111
- - When a query returns nulls or zero rows, explain what you checked and what went wrong
112
- - Narrate your progress between steps: "Found 3 data sources. Using **Marketing UA DS** (ds_abc123) which covers spend, installs, and ROAS columns."
113
-
114
- **Never do these:**
115
- - Run 3 queries silently then jump to building HTML
116
- - Say "I found the data" without showing what the data looks like
117
- - Present raw JSON output without formatting
118
- - Skip showing KB exploration results before proposing a dashboard
85
+ | Helper | Usage |
86
+ |---|---|
87
+ | `graphit.chart(el, {type, data, x, y, ...})` | Bar, line, area, donut, scatter, stacked-bar, heatmap, funnel, gauge, sparkline |
88
+ | `graphit.table(el, {data, columns?, ...})` | Styled HTML table |
89
+ | `graphit.kpi(el, {value, label?, format?})` | KPI card with optional delta |
119
90
 
120
- ---
91
+ `graphit.chart` types: `"bar"`, `"line"`, `"area"`, `"donut"` (alias `"pie"`), `"scatter"` (alias `"bubble"`), `"stacked-bar"` (alias `"stacked"`), `"heatmap"`, `"funnel"`, `"gauge"`, `"sparkline"`. Config: `x` (category field), `y` (value field), `series` (group-by), `title`, `height` (140-900px), `valueFormat` (`"currency"` | `"percent"` | `"number"`), `colors` (array). Scatter adds: `size`, `label`. Gauge adds: `min`, `max`, `format`. Sparkline adds: `width`, `showValue`.
121
92
 
122
- ## Chart Type Quick Reference
123
-
124
- | Data shape | Chart | Inline technique |
125
- |---|---|---|
126
- | 1 temporal + 1 numeric | Line | SVG path |
127
- | 1 categorical + 1 numeric | Bar | CSS width% divs |
128
- | Single number | KPI card | Styled div |
129
- | Part-whole (max 5) | Donut | Canvas arc |
130
- | Matrix / cohort | Heatmap | CSS grid + opacity |
131
- | Stages | Funnel | CSS width% bars, narrowing |
132
- | Time sparkline | Sparkline | Inline SVG path |
133
- | Detail / raw data | Table | HTML table + hover rows |
93
+ ### Canonical pattern - entity with live data
94
+
95
+ ```html
96
+ <div data-graphit-id="spend-by-source"
97
+ data-graphit-label="Ad Spend by Source"
98
+ data-graphit-kb="metric:CPI,dimension:MEDIA_SOURCE,table:MARKETING_UA"
99
+ data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA GROUP BY MEDIA_SOURCE ORDER BY spend DESC">
100
+ <div id="spend-chart"></div>
101
+ </div>
102
+ <script>
103
+ (async function() {
104
+ var r = await graphit.resolve({
105
+ sql: "SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) as spend FROM MARKETING_UA GROUP BY MEDIA_SOURCE ORDER BY spend DESC",
106
+ dataSourceId: "ds_abc123",
107
+ target: "#spend-chart"
108
+ });
109
+ graphit.chart("#spend-chart", {
110
+ type: "bar", data: r.data, x: "MEDIA_SOURCE", y: "spend",
111
+ title: "Ad Spend by Source", valueFormat: "currency"
112
+ });
113
+ })();
114
+ </script>
115
+ ```
134
116
 
135
117
  For detailed chart selection logic, dashboard planning, SQL reference, domain-specific lenses, Graphit visual style, and chart pattern implementations, see the companion rule files (graphit-planning, graphit-sql, graphit-lenses, graphit-style, graphit-kb, graphit-patterns).