vibepulse 0.1.0 → 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 (68) hide show
  1. package/README.md +7 -13
  2. package/bin/vibepulse.js +1 -0
  3. package/dist/index.js +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/docs/session-status-detection.md +258 -0
  6. package/next.config.ts +11 -0
  7. package/package.json +17 -11
  8. package/postcss.config.mjs +7 -0
  9. package/public/file.svg +1 -0
  10. package/public/globe.svg +1 -0
  11. package/public/next.svg +1 -0
  12. package/public/readme-cover.png +0 -0
  13. package/public/vercel.svg +1 -0
  14. package/public/window.svg +1 -0
  15. package/src/app/api/opencode-config/route.ts +304 -0
  16. package/src/app/api/opencode-config/status/route.ts +31 -0
  17. package/src/app/api/opencode-events/route.ts +86 -0
  18. package/src/app/api/opencode-models/route.test.ts +135 -0
  19. package/src/app/api/opencode-models/route.ts +58 -0
  20. package/src/app/api/profiles/[id]/apply/route.ts +49 -0
  21. package/src/app/api/profiles/[id]/route.ts +160 -0
  22. package/src/app/api/profiles/route.ts +107 -0
  23. package/src/app/api/sessions/[id]/archive/route.ts +35 -0
  24. package/src/app/api/sessions/[id]/delete/route.ts +26 -0
  25. package/src/app/api/sessions/[id]/route.ts +45 -0
  26. package/src/app/api/sessions/route.ts +596 -0
  27. package/src/app/favicon.ico +0 -0
  28. package/src/app/globals.css +66 -0
  29. package/src/app/layout.tsx +37 -0
  30. package/src/app/page.tsx +239 -0
  31. package/src/components/ErrorBoundary.tsx +72 -0
  32. package/src/components/KanbanBoard.tsx +442 -0
  33. package/src/components/LoadingState.tsx +37 -0
  34. package/src/components/ProjectCard.tsx +382 -0
  35. package/src/components/QueryProvider.tsx +25 -0
  36. package/src/components/SessionCard.tsx +291 -0
  37. package/src/components/SessionList.tsx +60 -0
  38. package/src/components/opencode-config/AgentConfigForm.test.tsx +66 -0
  39. package/src/components/opencode-config/AgentConfigForm.tsx +445 -0
  40. package/src/components/opencode-config/AgentModelSelector.tsx +284 -0
  41. package/src/components/opencode-config/AgentsConfigPanel.tsx +162 -0
  42. package/src/components/opencode-config/ConfigButton.tsx +43 -0
  43. package/src/components/opencode-config/ConfigPanel.tsx +91 -0
  44. package/src/components/opencode-config/FullscreenConfigPanel.tsx +360 -0
  45. package/src/components/opencode-config/categories/CategoriesList.tsx +328 -0
  46. package/src/components/opencode-config/categories/CategoriesManager.test.tsx +97 -0
  47. package/src/components/opencode-config/categories/CategoriesManager.tsx +174 -0
  48. package/src/components/opencode-config/categories/CategoryConfigForm.tsx +384 -0
  49. package/src/components/opencode-config/profiles/ProfileCard.tsx +140 -0
  50. package/src/components/opencode-config/profiles/ProfileEditor.tsx +446 -0
  51. package/src/components/opencode-config/profiles/ProfileList.tsx +398 -0
  52. package/src/components/opencode-config/profiles/ProfileManager.test.tsx +122 -0
  53. package/src/components/opencode-config/profiles/ProfileManager.tsx +293 -0
  54. package/src/components/ui/Tabs.tsx +59 -0
  55. package/src/hooks/useOpencodeSync.ts +378 -0
  56. package/src/index.ts +2 -0
  57. package/src/lib/notificationSound.ts +266 -0
  58. package/src/lib/opencodeConfig.test.ts +81 -0
  59. package/src/lib/opencodeConfig.ts +48 -0
  60. package/src/lib/opencodeDiscovery.ts +154 -0
  61. package/src/lib/profiles/storage.ts +264 -0
  62. package/src/lib/transform.ts +84 -0
  63. package/src/test/setup.ts +8 -0
  64. package/src/types/index.ts +89 -0
  65. package/src/types/opencodeConfig.ts +133 -0
  66. package/src/types/testing-library-vitest.d.ts +17 -0
  67. package/tsconfig.json +34 -0
  68. package/tsconfig.lib.json +17 -0
package/README.md CHANGED
@@ -37,25 +37,19 @@ Real-time dashboard for monitoring and managing OpenCode sessions.
37
37
 
38
38
  ## Getting Started
39
39
 
40
- ### Quick Start (npx)
41
-
42
- ```bash
43
- # Run directly without installing
44
- npx vibepulse
45
-
46
- # Or specify a custom port
47
- PORT=8080 npx vibepulse
48
- ```
49
-
50
- Open [http://localhost:3456](http://localhost:3456)
51
-
52
40
  ### Development
53
41
 
42
+ Clone and run from source:
43
+
54
44
  ```bash
45
+ # Clone the repository
46
+ git clone https://github.com/ChatTreeNet/VibePulse.git
47
+ cd VibePulse
48
+
55
49
  # Install dependencies
56
50
  npm install
57
51
 
58
- # Make sure OpenCode is running locally, then:
52
+ # Start development server
59
53
  npm run dev
60
54
 
61
55
  # Or specify a custom port
package/bin/vibepulse.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ /* eslint-disable @typescript-eslint/no-require-imports */
2
3
 
3
4
  const { spawn } = require('child_process');
4
5
  const path = require('path');
package/dist/index.js CHANGED
@@ -48,7 +48,7 @@ async function readConfig(configPath = CONFIG_PATH) {
48
48
  const content = await (0, import_promises.readFile)(configPath, "utf-8");
49
49
  const config = (0, import_comment_json.parse)(content, null, false);
50
50
  return config;
51
- } catch (error) {
51
+ } catch {
52
52
  return {};
53
53
  }
54
54
  }
package/dist/index.mjs CHANGED
@@ -18,7 +18,7 @@ async function readConfig(configPath = CONFIG_PATH) {
18
18
  const content = await readFile(configPath, "utf-8");
19
19
  const config = parse(content, null, false);
20
20
  return config;
21
- } catch (error) {
21
+ } catch {
22
22
  return {};
23
23
  }
24
24
  }
@@ -0,0 +1,258 @@
1
+ # VibePulse Session Status Detection
2
+
3
+ ## Overview
4
+
5
+ VibePulse uses a multi-layer detection mechanism to determine the real-time status of OpenCode sessions (idle/busy/retry). Since OpenCode's native status reporting is sparse and delayed, the system combines multiple signals to improve status accuracy.
6
+
7
+ ---
8
+
9
+ ## Core Concepts
10
+
11
+ ### Status Definitions
12
+
13
+ | Status | Meaning | Column |
14
+ |--------|---------|--------|
15
+ | `idle` | Idle / Completed | Idle Column |
16
+ | `busy` | Actively Running | Busy Column |
17
+ | `retry` | Waiting for User Input / Retry | Needs Attention Column |
18
+
19
+ ---
20
+
21
+ ## Detection Architecture
22
+
23
+ ```mermaid
24
+ flowchart TB
25
+ subgraph Input["Input Signal Layer"]
26
+ A1["OpenCode /session/status\nSparse, often empty"]
27
+ A2["Message Part Status\nrunning/completed/waiting"]
28
+ A3["Session Metadata\nupdated/created timestamps"]
29
+ end
30
+
31
+ subgraph Process["Status Processing Layer"]
32
+ B1["Part Status Analysis"]
33
+ B2["Sticky State Management\n25s one-way buffer"]
34
+ B3["Child Session Cascade"]
35
+ end
36
+
37
+ subgraph Output["Output Layer"]
38
+ C1["realTimeStatus"]
39
+ C2["waitingForUser"]
40
+ C3["Kanban Column Assignment"]
41
+ end
42
+
43
+ A1 --> B1
44
+ A2 --> B1
45
+ A3 --> B2
46
+ B1 --> B2
47
+ B2 --> C1
48
+ B2 --> B3
49
+ B3 --> C2
50
+ C1 --> C3
51
+ C2 --> C3
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Key Detection Mechanisms
57
+
58
+ ### 1. Part Status Analysis
59
+
60
+ Extracts part status from recent session messages to determine activity:
61
+
62
+ ```mermaid
63
+ flowchart TD
64
+ A["Fetch last 8 messages"] --> B{"Any running part?"}
65
+ B -->|Yes| C["Status = busy"]
66
+ B -->|No| D{"Any waiting part?"}
67
+ D -->|Yes| E["Status = retry\nwaitingForUser = true"]
68
+ D -->|No| F{"All parts completed?"}
69
+ F -->|Yes| G["Likely idle"]
70
+ F -->|No| H["Unknown state"]
71
+ ```
72
+
73
+ **Key Limitation**: Only checks the last 8 messages. Tasks with long periods of no output are misclassified as completed.
74
+
75
+ ---
76
+
77
+ ### 2. One-Way Sticky State (Core Mechanism)
78
+
79
+ Prevents status jitter using a **one-way buffering strategy**:
80
+
81
+ ```mermaid
82
+ flowchart LR
83
+ subgraph IdleToBusy["idle → busy"]
84
+ A1["Detected busy"] --> A2["Immediate effect"]
85
+ A2 --> A3["Update lastBusyAt = now"]
86
+ end
87
+
88
+ subgraph BusyToIdle["busy → idle"]
89
+ B1["Detected idle"] --> B2{"lastBusyAt\nwithin 25s?"}
90
+ B2 -->|Yes| B3["Keep busy\n(sticky)"]
91
+ B2 -->|No| B4["Actually become idle"]
92
+ end
93
+ ```
94
+
95
+ **Design Rationale**:
96
+ - `idle → busy`: **Immediate effect** (once busy is detected, it's truly running)
97
+ - `busy → idle`: **25-second buffer** (prevents misclassification from brief state loss)
98
+
99
+ ---
100
+
101
+ ### 3. Child Session Cascade
102
+
103
+ Parent session status is influenced by child sessions:
104
+
105
+ ```mermaid
106
+ flowchart TB
107
+ subgraph Parent["Parent Session"]
108
+ P1["realTimeStatus: idle"]
109
+ P2["But has child sessions"]
110
+ end
111
+
112
+ subgraph Children["Child Session States"]
113
+ C1["Child 1: busy"]
114
+ C2["Child 2: idle"]
115
+ C3["Child 3: retry"]
116
+ end
117
+
118
+ P1 --> D{"Any child session active?"}
119
+ C1 --> D
120
+ C2 --> D
121
+ C3 --> D
122
+
123
+ D -->|Yes| E["effectiveStatus = busy\nDisplay in Busy Column"]
124
+ D -->|No| F["effectiveStatus = idle\nDisplay in Idle Column"]
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Complete State Transition Flow
130
+
131
+ ```mermaid
132
+ sequenceDiagram
133
+ participant OC as OpenCode
134
+ participant API as /api/sessions
135
+ participant Sticky as Sticky State Manager
136
+ participant UI as Kanban UI
137
+
138
+ Note over OC,UI: Scenario: Long-running task
139
+
140
+ OC->>OC: Task starts (busy)
141
+ OC->>API: /session/status = busy
142
+ API->>Sticky: Update lastBusyAt
143
+ Sticky->>UI: Display busy
144
+
145
+ Note over OC,UI: 10 seconds later, OpenCode stops sending status
146
+
147
+ OC->>API: /session/status = null (sparse)
148
+ API->>Sticky: Query lastBusyAt
149
+ Sticky-->>Sticky: now - lastBusyAt = 10s < 25s
150
+ Sticky->>UI: **Keep busy** (sticky)
151
+
152
+ Note over OC,UI: 30 seconds later
153
+
154
+ API->>Sticky: Query lastBusyAt
155
+ Sticky-->>Sticky: now - lastBusyAt = 30s > 25s
156
+ Sticky->>UI: **Become idle**
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Detection Limitations (Shortcomings)
162
+
163
+ ### Root Cause: Unreliable Signal Source
164
+
165
+ ```mermaid
166
+ flowchart TB
167
+ subgraph RootCause["Root Cause: Insufficient OpenCode Signals"]
168
+ R1["No heartbeat mechanism\nCannot prove 'I am computing'"]
169
+ R2["Sparse status reporting\n/session/status often empty"]
170
+ R3["Discrete part status\nOnly running/completed, no progress"]
171
+ end
172
+
173
+ subgraph Workarounds["Current Workarounds"]
174
+ W1["Time buffer (25s sticky)"]
175
+ W2["Message history inference (8 messages)"]
176
+ W3["Child session cascade"]
177
+ end
178
+
179
+ RootCause --> Workarounds
180
+ ```
181
+
182
+ ### Specific Shortcomings
183
+
184
+ | Shortcoming | Impact Scenario | User Experience |
185
+ |-------------|-----------------|-----------------|
186
+ | **1. Shallow message sampling** | Long computation without output | Misclassified as idle, user thinks task finished |
187
+ | **2. Fixed time window** | One-size-fits-all approach | 25s may be too short for some tasks, too long for others |
188
+ | **3. No CPU/IO monitoring** | Process hang or deadlock | Continuously shows busy, user waits in vain |
189
+ | **4. Cannot detect deep subtask nesting** | Nested agent calls | Grandchild task status not trackable |
190
+ | **5. Network jitter sensitive** | Brief disconnection | May trigger unnecessary stale state |
191
+ | **6. Audio-visual out of sync** | Rapid status switching | Sound plays before/after card movement, disjointed experience |
192
+
193
+ ### Misclassification Example
194
+
195
+ ```mermaid
196
+ sequenceDiagram
197
+ participant User as User
198
+ participant UI as VibePulse Kanban
199
+ participant Task as Background Task
200
+
201
+ Note over User,Task: Scenario: Data analysis task running
202
+
203
+ User->>UI: Check kanban
204
+ UI->>Task: Query status
205
+ Task-->>UI: busy (second 1)
206
+ UI-->>User: Display Busy ✓
207
+
208
+ Note over User,Task: Task continues, but no new messages
209
+
210
+ Task->>Task: Continuous computation (no output)
211
+
212
+ Note over User,Task: After 25 seconds...
213
+
214
+ UI->>Task: Query status
215
+ Task-->>UI: No status / completed parts
216
+ UI-->>UI: Sticky window expired
217
+ UI-->>User: Display Idle ✗ (Misclassification!)
218
+
219
+ User->>User: Confused: Did the task finish?
220
+ ```
221
+
222
+ ### Improvement Directions (Not Implemented)
223
+
224
+ | Improvement | Difficulty | Impact | Priority |
225
+ |-------------|------------|--------|----------|
226
+ | Increase message sampling depth (50 messages) | Low | Reduce misclassification | P2 |
227
+ | Process-level CPU monitoring | Medium | Detect deadlocks | P1 |
228
+ | Adaptive time window (by task type) | Medium | Precise judgment | P3 |
229
+ | MCP Progress Token integration | High | Accurate progress | P1 (requires OpenCode support) |
230
+ | Heartbeat keepalive mechanism | High | Real-time status | P2 (requires protocol change) |
231
+
232
+ ---
233
+
234
+ ## Key Time Parameters
235
+
236
+ | Parameter | Value | Purpose |
237
+ |-----------|-------|---------|
238
+ | `STATUS_STICKY_BUSY_WINDOW_MS` | 25 seconds | Buffer window for busy → idle transition |
239
+ | `CHILD_ACTIVE_WINDOW_MS` | 30 minutes | Child session activity determination window |
240
+ | `CHILD_UNKNOWN_STATE_BUSY_WINDOW_MS` | 2 minutes | Busy assumption for unknown states |
241
+ | `STALL_DETECTION_WINDOW_MS` | 30 seconds | Stall detection (if updated time is within this window) |
242
+ | `STATUS_STICKY_RETENTION_MS` | 24 hours | Sticky state memory retention time |
243
+
244
+ ---
245
+
246
+ ## Data Flow Summary
247
+
248
+ ```mermaid
249
+ flowchart LR
250
+ A["OpenCode\nTrue State"] -->|Sparse signals| B("VibePulse\nMulti-layer detection")
251
+ B -->|Sticky buffering| C["Stable State"]
252
+ C -->|transform| D["Kanban Cards"]
253
+ D -->|Animation| E["UI Column Movement"]
254
+
255
+ F["Sound Alert"] -.->|Delayed 250ms| E
256
+ ```
257
+
258
+ **Core Design Philosophy**: Due to unreliable upstream (OpenCode) signals, the system provides **good enough** stability through **time buffering** and **multi-layer inference**, rather than absolute precision.
package/next.config.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ // Use a less common port to avoid conflicts
5
+ // Can be overridden with PORT env var
6
+ ...(process.env.PORT && {
7
+ // Next.js doesn't support port in config, use env var instead
8
+ }),
9
+ };
10
+
11
+ export default nextConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibepulse",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "description": "Real-time dashboard for monitoring and managing OpenCode sessions",
6
6
  "repository": {
@@ -12,6 +12,19 @@
12
12
  "files": [
13
13
  "dist",
14
14
  "bin",
15
+ "src",
16
+ "app",
17
+ "public",
18
+ "components",
19
+ "lib",
20
+ "hooks",
21
+ "types",
22
+ "docs",
23
+ "next.config.ts",
24
+ "tsconfig.json",
25
+ "tsconfig.lib.json",
26
+ "tailwind.config.ts",
27
+ "postcss.config.mjs",
15
28
  "README.md",
16
29
  "LICENSE"
17
30
  ],
@@ -22,16 +35,6 @@
22
35
  "require": "./dist/index.cjs"
23
36
  }
24
37
  },
25
- "peerDependencies": {
26
- "next": ">=14.0.0",
27
- "react": ">=18.0.0",
28
- "react-dom": ">=18.0.0"
29
- },
30
- "peerDependenciesMeta": {
31
- "next": {
32
- "optional": true
33
- }
34
- },
35
38
  "scripts": {
36
39
  "dev": "next dev -p 3456",
37
40
  "build": "next build",
@@ -56,6 +59,9 @@
56
59
  "clsx": "^2.1.1",
57
60
  "comment-json": "^4.6.2",
58
61
  "lucide-react": "^0.575.0",
62
+ "next": "^16.1.6",
63
+ "react": "^19.2.3",
64
+ "react-dom": "^19.2.3",
59
65
  "react-hook-form": "^7.71.2",
60
66
  "tailwind-merge": "^3.5.0"
61
67
  },
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
@@ -0,0 +1 @@
1
+ <svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
Binary file
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>