@quarri/claude-data-tools 1.1.0 → 1.1.1

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": "@quarri/claude-data-tools",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Quarri Data Assistant - Natural language data analysis with Quarri",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,12 +1,12 @@
1
1
  ---
2
- description: Generate charts in multiple formats - QuickChart URLs, matplotlib, or ASCII
2
+ description: Generate interactive Plotly charts and open them in the browser
3
3
  globs:
4
4
  alwaysApply: false
5
5
  ---
6
6
 
7
- # /quarri-chart - Flexible Chart Generation
7
+ # /quarri-chart - Interactive Chart Generation
8
8
 
9
- Generate data visualizations in multiple formats optimized for Claude Code users.
9
+ Generate data visualizations as interactive HTML files and open them in the default browser.
10
10
 
11
11
  ## When to Use
12
12
 
@@ -14,73 +14,71 @@ Use `/quarri-chart` when users want visualizations:
14
14
  - "Create a chart of revenue by month"
15
15
  - "Visualize customer distribution"
16
16
  - "Show me a graph of this data"
17
- - "What's the best way to display these results?"
17
+ - "Chart sales by category"
18
18
 
19
- ## Output Formats
19
+ ## Primary Workflow (DEFAULT)
20
20
 
21
- ### 1. QuickChart URL (DEFAULT)
22
-
23
- Generate a URL that renders instantly in any browser or terminal with image support.
24
-
25
- **When to use**: Quick visualization, sharing, embedding in documents
26
-
27
- ```
28
- https://quickchart.io/chart?c={encoded_config}
21
+ ### Step 1: Query the data
22
+ ```sql
23
+ SELECT category, SUM(sales) as total_sales
24
+ FROM orders
25
+ GROUP BY category
26
+ ORDER BY total_sales DESC
29
27
  ```
30
28
 
31
- **Example**:
32
- ```
33
- https://quickchart.io/chart?c={type:'bar',data:{labels:['Q1','Q2','Q3','Q4'],datasets:[{label:'Revenue',data:[120,150,180,200]}]}}
34
- ```
35
-
36
- ### 2. Matplotlib Code
37
-
38
- Generate Python code that creates and saves chart images.
39
-
40
- **When to use**: Custom styling, publication-quality charts, local files
41
-
42
- ```python
43
- import matplotlib.pyplot as plt
44
- import pandas as pd
45
-
46
- # Data
47
- categories = ['Q1', 'Q2', 'Q3', 'Q4']
48
- values = [120, 150, 180, 200]
49
-
50
- # Create figure
51
- fig, ax = plt.subplots(figsize=(10, 6))
52
- ax.bar(categories, values, color='#4F46E5')
53
-
54
- # Styling
55
- ax.set_xlabel('Quarter')
56
- ax.set_ylabel('Revenue ($K)')
57
- ax.set_title('Quarterly Revenue')
58
- ax.spines['top'].set_visible(False)
59
- ax.spines['right'].set_visible(False)
60
-
61
- # Save
62
- plt.tight_layout()
63
- plt.savefig('revenue_chart.png', dpi=150)
64
- plt.show()
29
+ ### Step 2: Generate HTML with Plotly
30
+ Write an HTML file to `/tmp/quarri_chart.html`:
31
+
32
+ ```html
33
+ <!DOCTYPE html>
34
+ <html>
35
+ <head>
36
+ <title>[Chart Title]</title>
37
+ <script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
38
+ <style>
39
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 40px; background: #f5f5f5; }
40
+ h1 { color: #1a1a2e; text-align: center; margin-bottom: 10px; }
41
+ .subtitle { color: #666; text-align: center; margin-bottom: 30px; }
42
+ #chart { width: 100%; max-width: 900px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); padding: 20px; }
43
+ </style>
44
+ </head>
45
+ <body>
46
+ <h1>[Chart Title]</h1>
47
+ <p class="subtitle">Data from [database_name]</p>
48
+ <div id="chart"></div>
49
+ <script>
50
+ var data = [{
51
+ x: [/* labels */],
52
+ y: [/* values */],
53
+ type: 'bar',
54
+ marker: { color: '#4F46E5' }
55
+ }];
56
+
57
+ var layout = {
58
+ title: '',
59
+ xaxis: { title: '[X Label]' },
60
+ yaxis: { title: '[Y Label]', tickformat: '$,.0f' },
61
+ margin: { t: 40, b: 60, l: 80, r: 40 }
62
+ };
63
+
64
+ var config = { responsive: true, displayModeBar: true };
65
+ Plotly.newPlot('chart', data, layout, config);
66
+ </script>
67
+ </body>
68
+ </html>
65
69
  ```
66
70
 
67
- ### 3. ASCII Chart
68
-
69
- Terminal-friendly text visualization for quick inspection.
70
-
71
- **When to use**: Terminal environments, quick data review, no graphics support
72
-
71
+ ### Step 3: Open in browser
72
+ ```bash
73
+ open /tmp/quarri_chart.html
73
74
  ```
74
- Revenue by Quarter
75
75
 
76
- Q1 |████████████ | $120K
77
- Q2 |███████████████ | $150K
78
- Q3 |██████████████████ | $180K
79
- Q4 |████████████████████ | $200K
80
- 0 100 200
81
- ```
76
+ On different platforms:
77
+ - **macOS**: `open /tmp/quarri_chart.html`
78
+ - **Linux**: `xdg-open /tmp/quarri_chart.html`
79
+ - **Windows**: `start /tmp/quarri_chart.html`
82
80
 
83
- ## Chart Selection Logic
81
+ ## Chart Type Selection
84
82
 
85
83
  ### Decision Tree
86
84
 
@@ -96,315 +94,276 @@ START: What is the primary analysis goal?
96
94
  └─→ 2-7 categories → VERTICAL BAR
97
95
  └─→ 8-15 categories → HORIZONTAL BAR
98
96
  └─→ 15+ categories → TOP N + "Other"
99
- └─→ Multiple dimensions → GROUPED BAR
100
97
 
101
98
  3. SHOWING DISTRIBUTION?
102
99
  └─→ Continuous data → HISTOGRAM
103
100
  └─→ Categorical → BAR (sorted)
104
- └─→ Compare groups → BOX PLOT
105
101
 
106
102
  4. PARTS OF A WHOLE?
107
103
  └─→ 2-6 parts → PIE/DONUT
108
- └─→ 7+ parts → STACKED BAR (100%)
104
+ └─→ 7+ parts → STACKED BAR
109
105
 
110
106
  5. RELATIONSHIP BETWEEN VARIABLES?
111
107
  └─→ Two numeric → SCATTER PLOT
112
- └─→ With grouping → SCATTER with colors
113
-
114
- DEFAULT: TABLE (when visualization unclear)
115
108
  ```
116
109
 
117
- ## QuickChart Configuration
110
+ ## Plotly Templates
118
111
 
119
112
  ### Bar Chart
120
113
  ```javascript
121
- {
122
- type: 'bar',
123
- data: {
124
- labels: ['A', 'B', 'C'],
125
- datasets: [{
126
- label: 'Values',
127
- data: [10, 20, 15],
128
- backgroundColor: '#4F46E5'
129
- }]
130
- },
131
- options: {
132
- title: { display: true, text: 'Chart Title' },
133
- legend: { display: false }
134
- }
135
- }
114
+ var data = [{
115
+ x: ['Category A', 'Category B', 'Category C'],
116
+ y: [450000, 380000, 290000],
117
+ type: 'bar',
118
+ marker: {
119
+ color: ['#4F46E5', '#7C3AED', '#A78BFA']
120
+ },
121
+ text: ['$450K', '$380K', '$290K'],
122
+ textposition: 'auto'
123
+ }];
124
+
125
+ var layout = {
126
+ title: 'Sales by Category',
127
+ xaxis: { title: 'Category' },
128
+ yaxis: { title: 'Sales ($)', tickformat: '$,.0f' }
129
+ };
136
130
  ```
137
131
 
138
- ### Line Chart
132
+ ### Horizontal Bar (many categories)
139
133
  ```javascript
140
- {
141
- type: 'line',
142
- data: {
143
- labels: ['Jan', 'Feb', 'Mar', 'Apr'],
144
- datasets: [{
145
- label: 'Revenue',
146
- data: [100, 120, 115, 140],
147
- borderColor: '#4F46E5',
148
- fill: false
149
- }]
150
- }
151
- }
134
+ var data = [{
135
+ y: ['Product A', 'Product B', 'Product C', 'Product D', 'Product E'],
136
+ x: [85000, 72000, 65000, 58000, 45000],
137
+ type: 'bar',
138
+ orientation: 'h',
139
+ marker: { color: '#4F46E5' }
140
+ }];
141
+
142
+ var layout = {
143
+ title: 'Top Products by Revenue',
144
+ xaxis: { title: 'Revenue ($)', tickformat: '$,.0f' },
145
+ margin: { l: 120 }
146
+ };
152
147
  ```
153
148
 
154
- ### Horizontal Bar
149
+ ### Line Chart (time series)
155
150
  ```javascript
156
- {
157
- type: 'horizontalBar',
158
- data: {
159
- labels: ['Product A', 'Product B', 'Product C'],
160
- datasets: [{
161
- data: [300, 250, 200],
162
- backgroundColor: ['#4F46E5', '#10B981', '#F59E0B']
163
- }]
164
- }
165
- }
151
+ var data = [{
152
+ x: ['2024-01', '2024-02', '2024-03', '2024-04', '2024-05', '2024-06'],
153
+ y: [120000, 135000, 128000, 145000, 160000, 175000],
154
+ type: 'scatter',
155
+ mode: 'lines+markers',
156
+ line: { color: '#4F46E5', width: 3 },
157
+ marker: { size: 8 }
158
+ }];
159
+
160
+ var layout = {
161
+ title: 'Monthly Revenue Trend',
162
+ xaxis: { title: 'Month' },
163
+ yaxis: { title: 'Revenue ($)', tickformat: '$,.0f' }
164
+ };
166
165
  ```
167
166
 
168
- ### Pie/Donut Chart
167
+ ### Multi-Line Chart
169
168
  ```javascript
170
- {
171
- type: 'doughnut',
172
- data: {
173
- labels: ['Desktop', 'Mobile', 'Tablet'],
174
- datasets: [{
175
- data: [60, 30, 10],
176
- backgroundColor: ['#4F46E5', '#10B981', '#F59E0B']
177
- }]
178
- }
179
- }
169
+ var data = [
170
+ {
171
+ x: ['Jan', 'Feb', 'Mar', 'Apr'],
172
+ y: [100, 120, 115, 140],
173
+ name: 'Product A',
174
+ type: 'scatter',
175
+ mode: 'lines+markers'
176
+ },
177
+ {
178
+ x: ['Jan', 'Feb', 'Mar', 'Apr'],
179
+ y: [80, 95, 110, 120],
180
+ name: 'Product B',
181
+ type: 'scatter',
182
+ mode: 'lines+markers'
183
+ }
184
+ ];
185
+
186
+ var layout = {
187
+ title: 'Product Comparison',
188
+ xaxis: { title: 'Month' },
189
+ yaxis: { title: 'Sales' }
190
+ };
180
191
  ```
181
192
 
182
- ### Scatter Plot
193
+ ### Pie/Donut Chart
183
194
  ```javascript
184
- {
185
- type: 'scatter',
186
- data: {
187
- datasets: [{
188
- label: 'Data Points',
189
- data: [{x: 1, y: 2}, {x: 2, y: 4}, {x: 3, y: 3}],
190
- backgroundColor: '#4F46E5'
191
- }]
192
- }
193
- }
195
+ var data = [{
196
+ labels: ['Technology', 'Furniture', 'Office Supplies'],
197
+ values: [5471124, 4730801, 4144724],
198
+ type: 'pie',
199
+ hole: 0.4, // Remove for regular pie
200
+ marker: {
201
+ colors: ['#4F46E5', '#10B981', '#F59E0B']
202
+ },
203
+ textinfo: 'label+percent'
204
+ }];
205
+
206
+ var layout = {
207
+ title: 'Sales Distribution by Category'
208
+ };
194
209
  ```
195
210
 
196
- ## Matplotlib Templates
197
-
198
- ### Bar Chart
199
- ```python
200
- import matplotlib.pyplot as plt
201
-
202
- fig, ax = plt.subplots(figsize=(10, 6))
203
- categories = ['A', 'B', 'C', 'D']
204
- values = [25, 40, 30, 35]
205
-
206
- bars = ax.bar(categories, values, color='#4F46E5', edgecolor='white')
207
- ax.bar_label(bars, fmt='%.0f')
208
-
209
- ax.set_ylabel('Value')
210
- ax.set_title('Category Comparison')
211
- ax.spines['top'].set_visible(False)
212
- ax.spines['right'].set_visible(False)
213
-
214
- plt.tight_layout()
215
- plt.savefig('bar_chart.png', dpi=150)
211
+ ### Scatter Plot
212
+ ```javascript
213
+ var data = [{
214
+ x: [/* x values */],
215
+ y: [/* y values */],
216
+ mode: 'markers',
217
+ type: 'scatter',
218
+ marker: {
219
+ size: 10,
220
+ color: '#4F46E5',
221
+ opacity: 0.7
222
+ }
223
+ }];
224
+
225
+ var layout = {
226
+ title: 'Correlation Analysis',
227
+ xaxis: { title: 'Variable X' },
228
+ yaxis: { title: 'Variable Y' }
229
+ };
216
230
  ```
217
231
 
218
- ### Line Chart
219
- ```python
220
- import matplotlib.pyplot as plt
221
- import matplotlib.dates as mdates
222
-
223
- fig, ax = plt.subplots(figsize=(12, 6))
224
- dates = ['2024-01', '2024-02', '2024-03', '2024-04']
225
- values = [100, 120, 115, 140]
226
-
227
- ax.plot(dates, values, marker='o', color='#4F46E5', linewidth=2, markersize=8)
228
- ax.fill_between(dates, values, alpha=0.1, color='#4F46E5')
229
-
230
- ax.set_ylabel('Revenue ($K)')
231
- ax.set_title('Monthly Revenue Trend')
232
- ax.grid(True, alpha=0.3)
233
- ax.spines['top'].set_visible(False)
234
- ax.spines['right'].set_visible(False)
235
-
236
- plt.tight_layout()
237
- plt.savefig('line_chart.png', dpi=150)
232
+ ### Grouped Bar Chart
233
+ ```javascript
234
+ var data = [
235
+ {
236
+ x: ['Q1', 'Q2', 'Q3', 'Q4'],
237
+ y: [120, 150, 180, 200],
238
+ name: '2023',
239
+ type: 'bar'
240
+ },
241
+ {
242
+ x: ['Q1', 'Q2', 'Q3', 'Q4'],
243
+ y: [140, 165, 195, 220],
244
+ name: '2024',
245
+ type: 'bar'
246
+ }
247
+ ];
248
+
249
+ var layout = {
250
+ title: 'Year over Year Comparison',
251
+ barmode: 'group'
252
+ };
238
253
  ```
239
254
 
240
- ### Horizontal Bar (for many categories)
241
- ```python
242
- import matplotlib.pyplot as plt
243
-
244
- fig, ax = plt.subplots(figsize=(10, 8))
245
- categories = ['Product A', 'Product B', 'Product C', 'Product D', 'Product E']
246
- values = [45, 38, 32, 28, 22]
247
-
248
- bars = ax.barh(categories, values, color='#4F46E5')
249
- ax.bar_label(bars, fmt='%.0f', padding=5)
255
+ ## Color Palettes
250
256
 
251
- ax.set_xlabel('Sales ($K)')
252
- ax.set_title('Top Products by Sales')
253
- ax.invert_yaxis() # Largest at top
254
- ax.spines['top'].set_visible(False)
255
- ax.spines['right'].set_visible(False)
257
+ ```javascript
258
+ // Primary (single series)
259
+ const PRIMARY = '#4F46E5';
256
260
 
257
- plt.tight_layout()
258
- plt.savefig('horizontal_bar.png', dpi=150)
259
- ```
261
+ // Categorical (multiple series)
262
+ const CATEGORICAL = ['#4F46E5', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899', '#06B6D4', '#84CC16'];
260
263
 
261
- ## ASCII Chart Generation
264
+ // Sequential (gradient)
265
+ const SEQUENTIAL = ['#E0E7FF', '#A5B4FC', '#818CF8', '#6366F1', '#4F46E5', '#4338CA', '#3730A3'];
262
266
 
263
- ### Horizontal Bar (ASCII)
267
+ // Diverging (positive/negative)
268
+ const DIVERGING_NEG = '#EF4444';
269
+ const DIVERGING_POS = '#10B981';
264
270
  ```
265
- def ascii_bar_chart(data, title, max_width=40):
266
- """Generate ASCII horizontal bar chart"""
267
- print(f"\n{title}\n")
268
- max_val = max(data.values())
269
271
 
270
- for label, value in data.items():
271
- bar_len = int((value / max_val) * max_width)
272
- bar = '█' * bar_len
273
- print(f"{label:>15} |{bar} {value:,.0f}")
272
+ ## Number Formatting
274
273
 
275
- print(f"{'':>15} {'─' * max_width}")
276
-
277
- # Example
278
- data = {'Electronics': 45000, 'Clothing': 32000, 'Home': 28000}
279
- ascii_bar_chart(data, "Revenue by Category")
280
- ```
274
+ ```javascript
275
+ // Currency
276
+ tickformat: '$,.0f' // $1,234,567
277
+ tickformat: '$,.2f' // $1,234,567.89
278
+ tickformat: '$~s' // $1.2M
281
279
 
282
- **Output:**
283
- ```
284
- Revenue by Category
280
+ // Percentages
281
+ tickformat: '.1%' // 45.2%
285
282
 
286
- Electronics |████████████████████████████████████████ 45,000
287
- Clothing |████████████████████████████ 32,000
288
- Home |████████████████████████ 28,000
289
- ────────────────────────────────────────
283
+ // Large numbers
284
+ tickformat: '~s' // 1.2M, 3.4K
290
285
  ```
291
286
 
292
- ### Vertical Bar (ASCII)
293
- ```
294
- def ascii_vertical_bar(data, title, height=10):
295
- """Generate ASCII vertical bar chart"""
296
- print(f"\n{title}\n")
297
- max_val = max(data.values())
298
-
299
- for row in range(height, 0, -1):
300
- line = ""
301
- for value in data.values():
302
- threshold = (row / height) * max_val
303
- line += " █ " if value >= threshold else " "
304
- print(f"{int(max_val * row / height):>6} |{line}")
305
-
306
- print(f"{'':>6} +{'─────' * len(data)}")
307
- labels = "".join(f"{k:^5}" for k in data.keys())
308
- print(f"{'':>8}{labels}")
309
-
310
- # Example
311
- data = {'Q1': 120, 'Q2': 150, 'Q3': 180, 'Q4': 200}
312
- ascii_vertical_bar(data, "Quarterly Revenue")
287
+ ## Complete Example
288
+
289
+ For "Show me sales by category":
290
+
291
+ ```html
292
+ <!DOCTYPE html>
293
+ <html>
294
+ <head>
295
+ <title>Sales by Category</title>
296
+ <script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
297
+ <style>
298
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 40px; background: #f5f5f5; }
299
+ h1 { color: #1a1a2e; text-align: center; margin-bottom: 10px; }
300
+ .subtitle { color: #666; text-align: center; margin-bottom: 30px; font-size: 14px; }
301
+ #chart { width: 100%; max-width: 900px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); padding: 20px; }
302
+ </style>
303
+ </head>
304
+ <body>
305
+ <h1>Superstore Sales by Category</h1>
306
+ <p class="subtitle">Total: $14.3M across 51,290 orders</p>
307
+ <div id="chart"></div>
308
+ <script>
309
+ var data = [{
310
+ x: ['Technology', 'Furniture', 'Office Supplies'],
311
+ y: [5471124.24, 4730801.23, 4144724.04],
312
+ type: 'bar',
313
+ marker: {
314
+ color: ['#4F46E5', '#7C3AED', '#A78BFA']
315
+ },
316
+ text: ['$5.47M', '$4.73M', '$4.14M'],
317
+ textposition: 'outside',
318
+ textfont: { size: 14, color: '#374151' }
319
+ }];
320
+
321
+ var layout = {
322
+ xaxis: { title: 'Category', tickfont: { size: 12 } },
323
+ yaxis: {
324
+ title: 'Sales ($)',
325
+ tickformat: '$,.0f',
326
+ tickfont: { size: 11 }
327
+ },
328
+ margin: { t: 40, b: 80, l: 100, r: 40 },
329
+ plot_bgcolor: 'white',
330
+ paper_bgcolor: 'white'
331
+ };
332
+
333
+ var config = {
334
+ responsive: true,
335
+ displayModeBar: true,
336
+ modeBarButtonsToRemove: ['lasso2d', 'select2d']
337
+ };
338
+
339
+ Plotly.newPlot('chart', data, layout, config);
340
+ </script>
341
+ </body>
342
+ </html>
313
343
  ```
314
344
 
315
- ### Sparkline (ASCII)
316
- ```
317
- def ascii_sparkline(values, width=30):
318
- """Generate inline ASCII sparkline"""
319
- chars = '▁▂▃▄▅▆▇█'
320
- min_val, max_val = min(values), max(values)
321
- range_val = max_val - min_val or 1
322
-
323
- line = ""
324
- for v in values:
325
- idx = int((v - min_val) / range_val * (len(chars) - 1))
326
- line += chars[idx]
327
-
328
- return f"[{line}] {values[0]:,.0f} → {values[-1]:,.0f}"
329
-
330
- # Example
331
- monthly = [100, 105, 98, 112, 120, 118, 125, 130, 128, 140, 145, 155]
332
- print(f"Revenue trend: {ascii_sparkline(monthly)}")
333
- # Output: Revenue trend: [▁▂▁▃▄▄▅▆▅▇▇█] 100 → 155
345
+ Then run:
346
+ ```bash
347
+ open /tmp/quarri_chart.html
334
348
  ```
335
349
 
336
- ## Workflow
337
-
338
- 1. **Analyze data shape**:
339
- - Count rows and columns
340
- - Identify column types (numeric, categorical, date)
341
- - Check value distributions
342
-
343
- 2. **Determine visualization goal**:
344
- - Parse user question for intent
345
- - Consider data characteristics
350
+ ## Alternative Outputs
346
351
 
347
- 3. **Select chart type**:
348
- - Apply decision tree
349
- - Consider data density and readability
350
-
351
- 4. **Choose output format**:
352
- - Default: QuickChart URL (instant rendering)
353
- - If user needs customization: matplotlib code
354
- - If terminal-only: ASCII chart
355
-
356
- 5. **Generate and present**:
357
- - Show chart or URL
358
- - Include rationale
359
- - Suggest alternatives
360
-
361
- ## Output Format
362
-
363
- ```markdown
364
- ## Visualization: [Data Description]
365
-
366
- ### Analysis
367
- - Data: [rows] rows, [columns] columns
368
- - Question type: [trend/comparison/distribution/etc.]
369
- - Key columns: [list]
370
-
371
- ### Recommended Chart: [Type]
372
- **Why**: [Brief rationale]
373
-
374
- ### Chart
375
-
376
- **QuickChart URL** (click to view):
377
- [URL]
378
-
379
- **Alternative: Matplotlib Code**
380
- ```python
381
- [Code block]
382
- ```
383
-
384
- **Alternative: ASCII Preview**
352
+ ### QuickChart URL (for sharing/embedding)
353
+ When user specifically needs a URL:
385
354
  ```
386
- [ASCII chart]
355
+ https://quickchart.io/chart?c={type:'bar',data:{labels:['A','B','C'],datasets:[{data:[10,20,15]}]}}
387
356
  ```
388
357
 
389
- ### Alternatives
390
- - [Alternative chart 1]: [When it might be better]
391
- - [Alternative chart 2]: [When it might be better]
358
+ ### ASCII Chart (terminal only)
359
+ When user is in a terminal-only environment:
392
360
  ```
361
+ Sales by Category
393
362
 
394
- ## Color Palettes
395
-
396
- ```python
397
- # Primary palette
398
- PRIMARY = '#4F46E5' # Indigo
399
-
400
- # Categorical (multiple series)
401
- CATEGORICAL = ['#4F46E5', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6', '#EC4899']
402
-
403
- # Sequential (single metric, gradient)
404
- SEQUENTIAL = ['#E0E7FF', '#A5B4FC', '#818CF8', '#6366F1', '#4F46E5']
405
-
406
- # Diverging (positive/negative)
407
- DIVERGING = ['#EF4444', '#FCA5A5', '#E5E7EB', '#86EFAC', '#22C55E']
363
+ Technology |████████████████████████████████████████ $5.47M
364
+ Furniture |██████████████████████████████████ $4.73M
365
+ Office Supplies |██████████████████████████████ $4.14M
366
+ 0 $3M $6M
408
367
  ```
409
368
 
410
369
  ## Integration