yangchenx commited on
Commit
e686056
·
verified ·
1 Parent(s): 2c88a7a

Create an app for a **deep research product** that functions like a **Jupyter notebook**, where each **agent or reasoning step** lives in its own **cell**.

Browse files

### Core Concept

* Each cell represents one **agent** or **processing step** (e.g., researcher, analyst, summarizer).
* A cell contains:

* Agent name or type
* Input prompt or references to previous cells
* Executable/re-runnable action button
* Structured output (text, markdown, table, chart, or JSON preview)
* The output of each cell can be passed as input to subsequent cells.
* Cells can be re-run independently to fix errors, fetch updated information, or explore variations.

### Features

1. **Notebook Layout**

* Vertical stack of cells, visually resembling a Jupyter notebook.
* Each cell shows:

* Agent header with name/icon.
* Editable input area.
* “Run” button.
* Output viewer (auto-formatted by type).
* Optional add/delete/reorder of cells.

2. **Output Visualization**

* Support multiple types of outputs:

* **Text/Markdown** → rendered in formatted text.
* **Tables/DataFrames** → rendered as interactive tables.
* **Charts** → simple visualization (e.g., bar/line chart).
* Each output should show a timestamp and status (success/error/loading).

3. **Cell Linking**

* Allow referencing outputs of previous cells as inputs (e.g., `{{cell_1.output}}`).
* Show simple dependency indicators or arrows.

4. **Re-run and Smart Update**

* Rerunning a cell updates its output.
* Optionally, downstream cells that depend on it can be re-run automatically or with confirmation.

5. **History and Versioning**

* Maintain version history per cell (view previous runs, revert).

6. **Overall Style**

* Minimalist, modern UI using **React + TailwindCSS**.
* Focus on clarity and traceability.
* Use cards or blocks for each cell with soft shadows and rounded corners.

### Deliverable

Generate a **React frontend prototype** (using TailwindCSS) implementing this interface.
Include:

* Components for `Notebook`, `Cell`, and `OutputViewer`.
* Mock data for several example cells.
* Interactive “Run” button simulation (can trigger mock updates).
The goal is to visually and functionally convey how agents operate as notebook-style cells in a deep research workflow.

Files changed (7) hide show
  1. README.md +8 -5
  2. components/cell.js +211 -0
  3. components/notebook.js +100 -0
  4. components/output-viewer.js +55 -0
  5. index.html +27 -19
  6. script.js +104 -0
  7. style.css +45 -19
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Agentic Research Notebook Wizard
3
- emoji: 👀
4
- colorFrom: indigo
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: Agentic Research Notebook Wizard ‍♂️
3
+ colorFrom: pink
4
+ colorTo: gray
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/cell.js ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class AgenticCell extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.attachShadow({ mode: 'open' });
5
+ }
6
+
7
+ static get observedAttributes() {
8
+ return ['agent-type', 'input', 'output', 'status', 'timestamp', 'has-previous'];
9
+ }
10
+
11
+ attributeChangedCallback(name, oldValue, newValue) {
12
+ if (oldValue !== newValue) {
13
+ this.render();
14
+ }
15
+ }
16
+
17
+ render() {
18
+ const agentType = this.getAttribute('agent-type') || 'researcher';
19
+ const input = this.getAttribute('input') || '';
20
+ const output = this.getAttribute('output') ? JSON.parse(this.getAttribute('output')) : null;
21
+ const status = this.getAttribute('status') || 'idle';
22
+ const timestamp = this.getAttribute('timestamp') || new Date().toISOString();
23
+ const hasPrevious = this.hasAttribute('has-previous');
24
+
25
+ const agentIcons = {
26
+ 'researcher': 'search',
27
+ 'analyst': 'bar-chart-2',
28
+ 'data-visualizer': 'pie-chart',
29
+ 'summarizer': 'file-text'
30
+ };
31
+
32
+ const statusColors = {
33
+ 'idle': 'bg-gray-200',
34
+ 'loading': 'bg-blue-200 animate-pulse-slow',
35
+ 'success': 'bg-green-200',
36
+ 'error': 'bg-red-200'
37
+ };
38
+
39
+ this.shadowRoot.innerHTML = `
40
+ <style>
41
+ :host {
42
+ display: block;
43
+ }
44
+
45
+ .cell-container {
46
+ border-radius: 0.5rem;
47
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
48
+ background-color: white;
49
+ overflow: hidden;
50
+ }
51
+
52
+ .cell-header {
53
+ display: flex;
54
+ align-items: center;
55
+ padding: 0.75rem 1rem;
56
+ background-color: #f9fafb;
57
+ border-bottom: 1px solid #e5e7eb;
58
+ }
59
+
60
+ .agent-icon {
61
+ display: flex;
62
+ align-items: center;
63
+ justify-content: center;
64
+ width: 2rem;
65
+ height: 2rem;
66
+ border-radius: 50%;
67
+ margin-right: 0.75rem;
68
+ }
69
+
70
+ .agent-name {
71
+ font-weight: 600;
72
+ color: #374151;
73
+ text-transform: capitalize;
74
+ }
75
+
76
+ .cell-status {
77
+ margin-left: auto;
78
+ font-size: 0.75rem;
79
+ padding: 0.25rem 0.5rem;
80
+ border-radius: 9999px;
81
+ }
82
+
83
+ .cell-timestamp {
84
+ margin-left: 0.75rem;
85
+ font-size: 0.75rem;
86
+ color: #6b7280;
87
+ }
88
+
89
+ .cell-content {
90
+ padding: 1rem;
91
+ }
92
+
93
+ .input-area {
94
+ width: 100%;
95
+ min-height: 3rem;
96
+ padding: 0.75rem;
97
+ border: 1px solid #e5e7eb;
98
+ border-radius: 0.375rem;
99
+ margin-bottom: 1rem;
100
+ font-family: inherit;
101
+ }
102
+
103
+ .input-area:focus {
104
+ outline: none;
105
+ border-color: #3b82f6;
106
+ box-shadow: 0 0 0 1px #3b82f6;
107
+ }
108
+
109
+ .actions {
110
+ display: flex;
111
+ justify-content: flex-end;
112
+ margin-bottom: 1rem;
113
+ }
114
+
115
+ .run-btn {
116
+ display: flex;
117
+ align-items: center;
118
+ gap: 0.5rem;
119
+ padding: 0.5rem 1rem;
120
+ background-color: #3b82f6;
121
+ color: white;
122
+ border: none;
123
+ border-radius: 0.375rem;
124
+ cursor: pointer;
125
+ transition: background-color 0.2s;
126
+ }
127
+
128
+ .run-btn:hover {
129
+ background-color: #2563eb;
130
+ }
131
+
132
+ .run-btn:disabled {
133
+ background-color: #9ca3af;
134
+ cursor: not-allowed;
135
+ }
136
+
137
+ .output-container {
138
+ border-top: 1px solid #e5e7eb;
139
+ padding-top: 1rem;
140
+ }
141
+
142
+ .output-header {
143
+ display: flex;
144
+ align-items: center;
145
+ margin-bottom: 0.75rem;
146
+ color: #4b5563;
147
+ font-weight: 500;
148
+ }
149
+
150
+ .empty-output {
151
+ color: #9ca3af;
152
+ font-style: italic;
153
+ padding: 1rem;
154
+ text-align: center;
155
+ }
156
+ </style>
157
+
158
+ <div class="cell-container cell-connector">
159
+ <div class="cell-header">
160
+ <div class="agent-icon bg-blue-100 text-blue-600">
161
+ <i data-feather="${agentIcons[agentType] || 'circle'}"></i>
162
+ </div>
163
+ <span class="agent-name">${agentType}</span>
164
+ ${status !== 'idle' ? `
165
+ <span class="cell-status ${statusColors[status]}">
166
+ ${status === 'loading' ? 'Running...' : status}
167
+ </span>
168
+ <span class="cell-timestamp">${new Date(timestamp).toLocaleString()}</span>
169
+ ` : ''}
170
+ </div>
171
+
172
+ <div class="cell-content">
173
+ <textarea class="input-area" placeholder="Enter your prompt or reference other cells with {{cell_id.output}}">${input}</textarea>
174
+
175
+ <div class="actions">
176
+ <button class="run-btn" ${status === 'loading' ? 'disabled' : ''}>
177
+ <i data-feather="play"></i>
178
+ Run
179
+ </button>
180
+ </div>
181
+
182
+ <div class="output-container">
183
+ ${output ? `
184
+ <div class="output-header">
185
+ <i data-feather="file-text" class="mr-2"></i>
186
+ Output
187
+ </div>
188
+ <agentic-output-viewer output='${JSON.stringify(output)}'></agentic-output-viewer>
189
+ ` : `
190
+ <div class="empty-output">No output yet. Run the cell to see results.</div>
191
+ `}
192
+ </div>
193
+ </div>
194
+ </div>
195
+ `;
196
+
197
+ // Add event listener for the run button
198
+ const runBtn = this.shadowRoot.querySelector('.run-btn');
199
+ runBtn.addEventListener('click', () => {
200
+ const cellId = this.getAttribute('id');
201
+ mockRunCell(cellId);
202
+ });
203
+
204
+ // Initialize feather icons
205
+ if (window.feather) {
206
+ window.feather.replace();
207
+ }
208
+ }
209
+ }
210
+
211
+ customElements.define('agentic-cell', AgenticCell);
components/notebook.js ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class AgenticNotebook extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.attachShadow({ mode: 'open' });
5
+ this.cells = [];
6
+ }
7
+
8
+ static get observedAttributes() {
9
+ return ['cells'];
10
+ }
11
+
12
+ attributeChangedCallback(name, oldValue, newValue) {
13
+ if (name === 'cells') {
14
+ this.cells = JSON.parse(newValue);
15
+ this.render();
16
+ }
17
+ }
18
+
19
+ render() {
20
+ this.shadowRoot.innerHTML = `
21
+ <style>
22
+ :host {
23
+ display: block;
24
+ width: 100%;
25
+ }
26
+
27
+ .notebook-header {
28
+ display: flex;
29
+ justify-content: space-between;
30
+ align-items: center;
31
+ margin-bottom: 1.5rem;
32
+ }
33
+
34
+ .add-cell-btn {
35
+ transition: all 0.2s ease;
36
+ }
37
+
38
+ .add-cell-btn:hover {
39
+ transform: translateY(-1px);
40
+ }
41
+
42
+ .cells-container {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: 1.5rem;
46
+ }
47
+ </style>
48
+
49
+ <div class="notebook-container">
50
+ <div class="notebook-header">
51
+ <h1 class="text-2xl font-bold text-gray-800">Agentic Research Notebook</h1>
52
+ <button class="add-cell-btn flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg shadow hover:bg-blue-700">
53
+ <i data-feather="plus"></i>
54
+ Add Cell
55
+ </button>
56
+ </div>
57
+
58
+ <div class="cells-container">
59
+ ${this.cells.map((cell, index) => `
60
+ <agentic-cell
61
+ id="${cell.id}"
62
+ agent-type="${cell.agentType}"
63
+ input="${cell.input}"
64
+ output='${JSON.stringify(cell.output)}'
65
+ status="${cell.status}"
66
+ timestamp="${cell.timestamp}"
67
+ ${index > 0 ? 'has-previous' : ''}
68
+ ></agentic-cell>
69
+ `).join('')}
70
+ </div>
71
+ </div>
72
+ `;
73
+
74
+ // Add event listener for the add cell button
75
+ const addCellBtn = this.shadowRoot.querySelector('.add-cell-btn');
76
+ addCellBtn.addEventListener('click', () => this.addNewCell());
77
+
78
+ // Initialize feather icons
79
+ if (window.feather) {
80
+ window.feather.replace();
81
+ }
82
+ }
83
+
84
+ addNewCell() {
85
+ const newCellId = `cell-${Date.now()}`;
86
+ const newCell = {
87
+ id: newCellId,
88
+ agentType: 'researcher',
89
+ input: '',
90
+ output: null,
91
+ status: 'idle',
92
+ timestamp: new Date().toISOString()
93
+ };
94
+
95
+ this.cells.push(newCell);
96
+ this.setAttribute('cells', JSON.stringify(this.cells));
97
+ }
98
+ }
99
+
100
+ customElements.define('agentic-notebook', AgenticNotebook);
components/output-viewer.js ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class AgenticOutputViewer extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.attachShadow({ mode: 'open' });
5
+ }
6
+
7
+ static get observedAttributes() {
8
+ return ['output'];
9
+ }
10
+
11
+ attributeChangedCallback(name, oldValue, newValue) {
12
+ if (name === 'output' && oldValue !== newValue) {
13
+ this.render();
14
+ }
15
+ }
16
+
17
+ render() {
18
+ const output = this.getAttribute('output') ? JSON.parse(this.getAttribute('output')) : null;
19
+
20
+ if (!output) {
21
+ this.shadowRoot.innerHTML = `<div>No output available</div>`;
22
+ return;
23
+ }
24
+
25
+ let content;
26
+
27
+ if (output.type === 'markdown') {
28
+ content = `
29
+ <div class="markdown-viewer p-4 bg-gray-50 rounded-lg">
30
+ ${this.renderMarkdown(output.content)}
31
+ </div>
32
+ `;
33
+ } else if (output.type === 'table') {
34
+ content = `
35
+ <div class="table-viewer overflow-x-auto">
36
+ <table class="min-w-full divide-y divide-gray-200">
37
+ <thead class="bg-gray-50">
38
+ <tr>
39
+ ${output.content.headers.map(header => `
40
+ <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
41
+ ${header}
42
+ </th>
43
+ `).join('')}
44
+ </tr>
45
+ </thead>
46
+ <tbody class="bg-white divide-y divide-gray-200">
47
+ ${output.content.rows.map((row, rowIndex) => `
48
+ <tr class="${rowIndex % 2 === 0 ? 'bg-white' : 'bg-gray-50'}">
49
+ ${row.map(cell => `
50
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
51
+ ${cell}
52
+ </td>
53
+ `).join('')}
54
+ </tr>
55
+ `).join('')}
index.html CHANGED
@@ -1,19 +1,27 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Agentic Research Notebook</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://unpkg.com/feather-icons"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
11
+ <link rel="stylesheet" href="style.css">
12
+ </head>
13
+ <body class="font-inter bg-gray-50 min-h-screen">
14
+ <div id="root" class="container mx-auto px-4 py-8">
15
+ <!-- Notebook will be rendered here -->
16
+ </div>
17
+
18
+ <script src="components/notebook.js"></script>
19
+ <script src="components/cell.js"></script>
20
+ <script src="components/output-viewer.js"></script>
21
+ <script src="script.js"></script>
22
+ <script>
23
+ feather.replace();
24
+ </script>
25
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
26
+ </body>
27
+ </html>
script.js ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ const root = document.getElementById('root');
3
+
4
+ // Mock data for cells
5
+ const initialCells = [
6
+ {
7
+ id: 'cell-1',
8
+ agentType: 'researcher',
9
+ input: 'Find recent breakthroughs in quantum computing',
10
+ output: {
11
+ type: 'markdown',
12
+ content: '## Quantum Computing Breakthroughs (2023)\n\n1. **IBM Quantum Heron Processor** - 133-qubit processor with improved error rates\n2. **Google Quantum Supremacy 2.0** - Demonstrated 70-qubit processor\n3. **Microsoft Topological Qubits** - More stable qubit design\n4. **Quantum Networking** - First multi-node quantum network demonstrated'
13
+ },
14
+ status: 'success',
15
+ timestamp: new Date(Date.now() - 3600000).toISOString()
16
+ },
17
+ {
18
+ id: 'cell-2',
19
+ agentType: 'analyst',
20
+ input: 'Summarize the key points from cell-1 into bullet points',
21
+ output: {
22
+ type: 'markdown',
23
+ content: '- IBM released 133-qubit processor\n- Google demonstrated 70-qubit processor\n- Microsoft developed more stable qubit design\n- Multi-node quantum networks achieved'
24
+ },
25
+ status: 'success',
26
+ timestamp: new Date(Date.now() - 1800000).toISOString()
27
+ },
28
+ {
29
+ id: 'cell-3',
30
+ agentType: 'data-visualizer',
31
+ input: 'Create a comparison table of quantum processors from cell-1',
32
+ output: {
33
+ type: 'table',
34
+ content: {
35
+ headers: ['Company', 'Qubits', 'Key Feature'],
36
+ rows: [
37
+ ['IBM', '133', 'Improved error rates'],
38
+ ['Google', '70', 'Supremacy 2.0'],
39
+ ['Microsoft', 'N/A', 'Topological stability']
40
+ ]
41
+ }
42
+ },
43
+ status: 'success',
44
+ timestamp: new Date(Date.now() - 900000).toISOString()
45
+ }
46
+ ];
47
+
48
+ // Create notebook with initial cells
49
+ const notebook = document.createElement('agentic-notebook');
50
+ notebook.setAttribute('cells', JSON.stringify(initialCells));
51
+ root.appendChild(notebook);
52
+ });
53
+
54
+ // Mock run function for cells
55
+ function mockRunCell(cellId) {
56
+ const cell = document.querySelector(`agentic-cell[id="${cellId}"]`);
57
+ if (!cell) return;
58
+
59
+ // Show loading state
60
+ cell.setAttribute('status', 'loading');
61
+
62
+ // Simulate API call delay
63
+ setTimeout(() => {
64
+ // Update with mock response
65
+ const randomSuccess = Math.random() > 0.1; // 90% success rate
66
+ const timestamp = new Date().toISOString();
67
+
68
+ if (randomSuccess) {
69
+ const agentType = cell.getAttribute('agent-type');
70
+ let output;
71
+
72
+ if (agentType === 'researcher') {
73
+ output = {
74
+ type: 'markdown',
75
+ content: '## Updated Research Results\n\nNew findings suggest quantum error correction is improving faster than expected.'
76
+ };
77
+ } else if (agentType === 'analyst') {
78
+ output = {
79
+ type: 'markdown',
80
+ content: '- Quantum error correction improving rapidly\n- New benchmarks show 2x improvement'
81
+ };
82
+ } else if (agentType === 'data-visualizer') {
83
+ output = {
84
+ type: 'table',
85
+ content: {
86
+ headers: ['Metric', 'Improvement'],
87
+ rows: [
88
+ ['Error Rate', '50% reduction'],
89
+ ['Speed', '2x faster'],
90
+ ['Stability', '3x more stable']
91
+ ]
92
+ }
93
+ };
94
+ }
95
+
96
+ cell.setAttribute('output', JSON.stringify(output));
97
+ cell.setAttribute('status', 'success');
98
+ } else {
99
+ cell.setAttribute('status', 'error');
100
+ }
101
+
102
+ cell.setAttribute('timestamp', timestamp);
103
+ }, 1500);
104
+ }
style.css CHANGED
@@ -1,28 +1,54 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
 
 
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
 
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @keyframes pulse {
2
+ 0%, 100% {
3
+ opacity: 1;
4
+ }
5
+ 50% {
6
+ opacity: 0.5;
7
+ }
8
  }
9
 
10
+ .animate-pulse-slow {
11
+ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
 
12
  }
13
 
14
+ .cell-connector {
15
+ position: relative;
 
 
 
16
  }
17
 
18
+ .cell-connector::before {
19
+ content: "";
20
+ position: absolute;
21
+ left: 24px;
22
+ top: -16px;
23
+ height: 16px;
24
+ width: 2px;
25
+ background-color: #e5e7eb;
26
  }
27
 
28
+ .json-viewer {
29
+ font-family: 'Courier New', monospace;
30
+ font-size: 0.875rem;
31
+ line-height: 1.5;
32
+ white-space: pre-wrap;
33
  }
34
+
35
+ .markdown-viewer h1 {
36
+ font-size: 1.5rem;
37
+ font-weight: 600;
38
+ margin: 1rem 0;
39
+ }
40
+
41
+ .markdown-viewer h2 {
42
+ font-size: 1.25rem;
43
+ font-weight: 600;
44
+ margin: 0.75rem 0;
45
+ }
46
+
47
+ .markdown-viewer p {
48
+ margin: 0.5rem 0;
49
+ }
50
+
51
+ .markdown-viewer ul, .markdown-viewer ol {
52
+ margin: 0.5rem 0;
53
+ padding-left: 1.5rem;
54
+ }