@graphit/cli 0.1.18 → 0.1.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphit/cli",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Graphit CLI - Build custom dashboards from any AI coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -43,7 +43,7 @@ Your HTML must NEVER contain:
43
43
  - `<img src="https://...">` - no external images
44
44
 
45
45
  If you use ANY `src=` or `href=` pointing to a URL, the dashboard will be blank.
46
- 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.
46
+ 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`, `graphit.presentation`) for standard visualizations - use it instead of importing libraries.
47
47
 
48
48
  ### 2. ALWAYS query through data sources
49
49
  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.
@@ -322,6 +322,7 @@ The iframe also provides optional convenience helpers if you want quick standard
322
322
  | `graphit.chart(el, {type, data, x, y, ...})` | Bar, line, area, donut, scatter, stacked-bar, heatmap, funnel, gauge, sparkline |
323
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])` |
324
324
  | `graphit.kpi(el, {value, label?, format?})` | KPI card with optional delta |
325
+ | `graphit.presentation(el)` | Full-screen slide deck. Returns builder: `.slide({bg, layout, html})` then `.start()`. See `presentations.md` |
325
326
 
326
327
  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.
327
328
 
@@ -378,3 +379,4 @@ Detailed knowledge lives in `references/`. Consult the relevant file when you ne
378
379
  | `graphit-style.md` | Building the HTML. Design principles, typography scale, color system with usage rules, layout patterns (page structure, KPI cards, data tables). |
379
380
  | `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
381
  | `governance.md` | Query governance. Reference syntax, trust tiers, enforceable rules, override flow, governance commands. |
382
+ | `presentations.md` | Slide deck presentations. Builder API, layouts (center/split/full), backgrounds, navigation, live data inside slides. |
@@ -0,0 +1,56 @@
1
+ ---
2
+ alwaysApply: false
3
+ description: "Graphit: Build full-screen slide deck presentations inside canvas dashboards using graphit.presentation(). Builder pattern with layouts, backgrounds, and keyboard navigation."
4
+ globs: []
5
+ ---
6
+
7
+ # Presentations
8
+
9
+ Build slide deck presentations inside Graphit canvas dashboards using `graphit.presentation()`.
10
+
11
+ ## API
12
+
13
+ ```js
14
+ var deck = graphit.presentation('#my-deck');
15
+ deck.slide({ bg: 'dark', layout: 'center', html: '<h1>Title</h1>' });
16
+ deck.slide({ bg: 'white', layout: 'split', html: '<div>Left</div><div>Right</div>' });
17
+ deck.slide({ bg: 'paper', layout: 'full', html: '<div id="charts">...</div>' });
18
+ var ctrl = deck.start();
19
+ ```
20
+
21
+ ## Slide Config
22
+
23
+ | Field | Type | Default | Description |
24
+ |-------|------|---------|-------------|
25
+ | `layout` | `'center'` / `'split'` / `'full'` | `'center'` | Content arrangement |
26
+ | `bg` | `'paper'` / `'white'` / `'dark'` / `'teal'` / hex | `'paper'` | Fixed background |
27
+ | `html` | `string` | `''` | Raw HTML content |
28
+
29
+ ## Layouts
30
+
31
+ - **center**: Centered horizontally/vertically. Title slides, CTAs.
32
+ - **split**: First two child elements become left/right panels. Text + image.
33
+ - **full**: Top-to-bottom flow. Tables, grids, live data.
34
+
35
+ ## Backgrounds (fixed, not theme-aware)
36
+
37
+ | Name | Background | Text |
38
+ |------|-----------|------|
39
+ | `paper` | `#F7F6F2` | `#222224` |
40
+ | `white` | `#FFFFFF` | `#222224` |
41
+ | `dark` | `#222224` | `#F7F6F2` |
42
+ | `teal` | `#4DB6AC` | `#FFFFFF` |
43
+
44
+ ## Navigation
45
+
46
+ Arrow keys, spacebar, Home/End. Prev/Next buttons bottom-right with slide counter.
47
+
48
+ ## Live data works inside slides
49
+
50
+ `graphit.resolve()` + `graphit.chart/table/kpi` work inside slides. Entity wrapping (`data-graphit-*`) works normally.
51
+
52
+ ## Anti-Patterns
53
+
54
+ - Don't use external fonts/images (CSP blocks). Embed as base64 or use system fonts.
55
+ - Don't put scrollable content on slides. Split into multiple slides.
56
+ - One `graphit.presentation()` per dashboard.
@@ -128,12 +128,14 @@ Canvas `graphit.resolve()` queries that follow these shapes serve from a semanti
128
128
  - Literal dates (`>= '2026-01-01'`), never `CURRENT_DATE` expressions
129
129
  - GROUP BY column names or ordinals; ORDER BY / LIMIT allowed (outer only)
130
130
  - CTEs are fine when the CTE body follows the same rules
131
+ - Top-N rank queries: project the sort metric in SELECT (`SELECT dim, SUM(metric) AS rv ... ORDER BY rv DESC LIMIT N`), not only in ORDER BY
131
132
 
132
133
  **Shapes that skip the cache** (fall back to normal execution, still correct):
133
134
  - `COUNT(DISTINCT x)`, window functions, HAVING, QUALIFY
134
135
  - OR / NOT in WHERE
135
136
  - Ratio metrics (`SUM(a)/NULLIF(SUM(b),0)`) - compute client-side or use two resolves
136
137
  - `CURRENT_DATE`-relative predicates
138
+ - Top-N with aggregate only in ORDER BY (`SELECT dim FROM t GROUP BY dim ORDER BY SUM(metric) DESC LIMIT N` - no decomposable aggregate in SELECT)
137
139
 
138
140
  ## Data Source Routing
139
141
 
@@ -37,6 +37,17 @@ NEVER use hardcoded hex colors. Use CSS custom properties that automatically ada
37
37
  - **Warning:** `var(--graphit-warning)`
38
38
  - **Danger / bad:** `var(--graphit-error)`
39
39
 
40
+ ### Custom Colors
41
+
42
+ When the user explicitly requests specific colors or a custom color theme, ask for both light and dark mode variants. Write explicit CSS with `.dark` class overrides so the dashboard adapts to both modes:
43
+
44
+ ```css
45
+ .revenue-card { background: #001f3f; color: #ffffff; }
46
+ .dark .revenue-card { background: #0a3d6b; color: #e0e0e0; }
47
+ ```
48
+
49
+ NEVER hardcode a structural color (background, color, border-color) without a `.dark` counterpart. Data/chart series colors (bar fills, line strokes) are exempt - they work in both modes.
50
+
40
51
  ### Multi-Series
41
52
  Use `var(--graphit-accent)` as the primary series. For additional series, choose muted complementary colors that work on both light and dark backgrounds.
42
53
 
@@ -0,0 +1,143 @@
1
+ # Presentations
2
+
3
+ Build full-screen slide deck presentations inside Graphit canvas dashboards using `graphit.presentation()`.
4
+
5
+ ## API
6
+
7
+ ```js
8
+ var deck = graphit.presentation('#my-deck');
9
+
10
+ deck.slide({ bg: 'dark', layout: 'center', html: '<h1>Title</h1><p>Subtitle</p>' });
11
+ deck.slide({ bg: 'white', layout: 'split', html: '<div>Left panel</div><div>Right panel</div>' });
12
+ deck.slide({ bg: 'paper', layout: 'full', html: '<div id="charts">...</div>' });
13
+
14
+ var ctrl = deck.start();
15
+ // ctrl.go(3) - jump to slide 4
16
+ // ctrl.total - number of slides
17
+ ```
18
+
19
+ `graphit.presentation(el)` returns a **builder**. Call `.slide()` to add slides (chainable), then `.start()` to render and wire navigation. `start()` returns a controller with `go(n)` and `total`.
20
+
21
+ ## Slide Config
22
+
23
+ | Field | Type | Default | Description |
24
+ |-------|------|---------|-------------|
25
+ | `layout` | `'center'` / `'split'` / `'full'` | `'center'` | How content is arranged inside the slide |
26
+ | `bg` | `'paper'` / `'white'` / `'dark'` / `'teal'` / hex | `'paper'` | Fixed background color (not theme-aware) |
27
+ | `html` | `string` | `''` | Raw HTML content for the slide |
28
+
29
+ ## Layouts
30
+
31
+ ### `center` (default)
32
+ Content is centered horizontally and vertically. Use for title slides, quotes, big stats, CTAs.
33
+
34
+ ### `split`
35
+ The first two child elements in `html` become left and right panels (50/50 flex). Use for text + image, comparison, side-by-side content. On mobile (<700px), panels stack vertically.
36
+
37
+ ```js
38
+ deck.slide({
39
+ layout: 'split',
40
+ bg: 'white',
41
+ html: '<div><h2>Left Title</h2><p>Text content</p></div><div><img src="data:..."></div>'
42
+ });
43
+ ```
44
+
45
+ ### `full`
46
+ Content flows top-to-bottom with generous padding. Use for tables, grids, live data dashboards, complex layouts that need the full slide width.
47
+
48
+ ## Background Themes
49
+
50
+ Backgrounds are **fixed colors** - they do not change with the app's light/dark mode. A dark slide stays dark. Content inside CAN use `var(--graphit-*)` tokens.
51
+
52
+ | Name | Background | Text Color |
53
+ |------|-----------|------------|
54
+ | `paper` | `#F7F6F2` (warm cream) | `#222224` (charcoal) |
55
+ | `white` | `#FFFFFF` | `#222224` |
56
+ | `dark` | `#222224` (charcoal) | `#F7F6F2` (cream) |
57
+ | `teal` | `#4DB6AC` (brand teal) | `#FFFFFF` |
58
+ | hex string | Custom (e.g. `#1a1a2e`) | Inherited from content HTML |
59
+
60
+ ## Navigation
61
+
62
+ The slide deck includes built-in navigation:
63
+ - **Arrow keys**: Left/Right to navigate (guarded - skips when focus is on input/textarea)
64
+ - **Spacebar**: Next slide
65
+ - **Home/End**: First/last slide
66
+ - **Prev/Next buttons**: Bottom-right corner with slide counter
67
+
68
+ ## Live Data Inside Slides
69
+
70
+ `graphit.resolve()` and `graphit.chart/table/kpi` work inside slides. All resolve calls fire on page load (cached queries are fast). Charts render into their target elements regardless of which slide is visible.
71
+
72
+ ```js
73
+ // Slide with live entities
74
+ deck.slide({
75
+ layout: 'full',
76
+ bg: 'white',
77
+ html: `
78
+ <h2>Live Data</h2>
79
+ <div data-graphit-id="spend-chart" data-graphit-label="Ad Spend"
80
+ data-graphit-kb="metric:TOTAL_AD_SPEND,table:MARKETING_UA"
81
+ data-graphit-sql="SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) AS spend FROM MARKETING_UA_DS GROUP BY 1 ORDER BY spend DESC LIMIT 6"
82
+ data-graphit-ds="ds_abc123">
83
+ <div id="chart1" class="gh-loading">
84
+ <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="#e5e5e5" stroke-width="2.5"/><path d="M12 2a10 10 0 0 1 10 10" stroke="#4DB6AC" stroke-width="2.5" stroke-linecap="round"/></svg></div>
85
+ </div>
86
+ </div>
87
+ `
88
+ });
89
+
90
+ // After deck.start(), fire resolve calls
91
+ deck.start();
92
+
93
+ graphit.resolve({
94
+ sql: "SELECT MEDIA_SOURCE, SUM(APPSFLYER_COST) AS spend FROM MARKETING_UA_DS GROUP BY 1 ORDER BY spend DESC LIMIT 6",
95
+ dataSourceId: "ds_abc123",
96
+ target: "#chart1"
97
+ }).then(function(r) {
98
+ graphit.chart("#chart1", { type: "bar", data: r.data, x: "MEDIA_SOURCE", y: "spend", valueFormat: "currency" });
99
+ });
100
+ ```
101
+
102
+ ## Entity Wrapping
103
+
104
+ Every data element inside slides should have full `data-graphit-*` entity wrapping, same as regular dashboards. The entity provenance panel, @ mentions, and click-to-inspect all work inside slides.
105
+
106
+ ## Example: Complete Pitch Deck
107
+
108
+ ```js
109
+ var deck = graphit.presentation('#deck');
110
+
111
+ deck.slide({
112
+ bg: 'paper',
113
+ layout: 'center',
114
+ html: '<h1 style="font-size:6rem;font-weight:300">Company Update</h1><p style="font-size:1.5rem;color:#6B7280;margin-top:16px">Q2 2026</p>'
115
+ });
116
+
117
+ deck.slide({
118
+ bg: 'dark',
119
+ layout: 'split',
120
+ html: '<div style="color:#F7F6F2"><h2 style="font-size:3rem">The Problem</h2><p style="font-size:1.25rem;opacity:0.7;margin-top:16px">Analysts spend 80% of their time assembling dashboards instead of finding insights.</p></div><div style="display:flex;align-items:center;justify-content:center"><img src="data:image/webp;base64,..." style="max-height:60vh;border-radius:16px"></div>'
121
+ });
122
+
123
+ deck.slide({
124
+ bg: 'white',
125
+ layout: 'full',
126
+ html: '<h2 style="font-size:2.5rem;margin-bottom:24px">Key Metrics</h2><div style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px">...</div>'
127
+ });
128
+
129
+ deck.slide({
130
+ bg: 'teal',
131
+ layout: 'center',
132
+ html: '<h2 style="font-size:4rem;color:#fff">Thank You</h2><p style="font-size:1.25rem;color:rgba(255,255,255,0.7);margin-top:16px">company.com</p>'
133
+ });
134
+
135
+ deck.start();
136
+ ```
137
+
138
+ ## Anti-Patterns
139
+
140
+ - **Don't put too much content on one slide.** If it scrolls, split into multiple slides.
141
+ - **Don't use external fonts via @import or link.** The iframe CSP blocks them. Embed as base64 @font-face or use system fonts.
142
+ - **Don't use external images.** Use data URIs (base64) or inline SVG.
143
+ - **Don't nest presentations.** One `graphit.presentation()` per dashboard.
@@ -122,12 +122,14 @@ Canvas `graphit.resolve()` queries that follow these shapes serve from a semanti
122
122
  - Literal dates (`>= '2026-01-01'`), never `CURRENT_DATE` expressions
123
123
  - GROUP BY column names or ordinals; ORDER BY / LIMIT allowed (outer only)
124
124
  - CTEs are fine when the CTE body follows the same rules
125
+ - Top-N rank queries: project the sort metric in SELECT (`SELECT dim, SUM(metric) AS rv ... ORDER BY rv DESC LIMIT N`), not only in ORDER BY
125
126
 
126
127
  **Shapes that skip the cache** (fall back to normal execution, still correct):
127
128
  - `COUNT(DISTINCT x)`, window functions, HAVING, QUALIFY
128
129
  - OR / NOT in WHERE
129
130
  - Ratio metrics (`SUM(a)/NULLIF(SUM(b),0)`) - compute client-side or use two resolves
130
131
  - `CURRENT_DATE`-relative predicates
132
+ - Top-N with aggregate only in ORDER BY (`SELECT dim FROM t GROUP BY dim ORDER BY SUM(metric) DESC LIMIT N` - no decomposable aggregate in SELECT)
131
133
 
132
134
  ## Data Source Routing
133
135