vidpipe 1.3.3 → 1.3.5

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.
@@ -81,6 +81,21 @@
81
81
  .platform-tab { transition: all 0.2s ease; }
82
82
  .platform-tab.active { border-bottom: 2px solid currentColor; }
83
83
 
84
+ .badge {
85
+ display: inline-flex;
86
+ align-items: center;
87
+ gap: 4px;
88
+ padding: 2px 8px;
89
+ border-radius: 12px;
90
+ font-size: 12px;
91
+ font-weight: 600;
92
+ white-space: nowrap;
93
+ }
94
+ .badge-danger { background: #f85149; color: white; }
95
+ .badge-warning { background: #d29922; color: white; }
96
+ .badge-success { background: #3fb950; color: white; }
97
+ .badge-info { background: #58a6ff; color: white; }
98
+
84
99
  body { background: #0f0f23; }
85
100
  </style>
86
101
  </head>
@@ -127,6 +142,49 @@
127
142
  } catch { return iso }
128
143
  }
129
144
 
145
+ function getGroupTitle(group) {
146
+ const raw = group.sourceClip || group.sourceVideo || 'Queued post'
147
+ const segments = String(raw).split(/[\\/]/).filter(Boolean)
148
+ return segments[segments.length - 1] || raw
149
+ }
150
+
151
+ function getGroupPlatforms(group) {
152
+ return [...new Set(group.items.map(item => {
153
+ const platform = normalizePlatform(item.metadata.platform)
154
+ return PLATFORMS[platform]?.name || item.metadata.platform
155
+ }))]
156
+ }
157
+
158
+ function getGroupIdeaPublishBy(group) {
159
+ const publishByDates = group.items
160
+ .map(item => item.ideaPublishBy)
161
+ .filter(Boolean)
162
+ .sort()
163
+ return publishByDates[0]
164
+ }
165
+
166
+ function TimelinessBadge({ publishBy }) {
167
+ if (!publishBy) return null
168
+
169
+ const now = new Date()
170
+ const deadline = new Date(publishBy)
171
+ if (Number.isNaN(deadline.getTime())) return null
172
+
173
+ const daysLeft = Math.ceil((deadline - now) / (1000 * 60 * 60 * 24))
174
+ const formattedDeadline = deadline.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })
175
+
176
+ if (daysLeft < 0) {
177
+ return html`<span class="badge badge-danger" title="Trend window has passed">⚠️ Trend expired</span>`
178
+ }
179
+ if (daysLeft <= 7) {
180
+ return html`<span class="badge badge-warning" title=${`Publish within ${daysLeft} days`}>⚡ ${daysLeft}d window</span>`
181
+ }
182
+ if (daysLeft > 30) {
183
+ return html`<span class="badge badge-success" title="Evergreen content">🌿 Evergreen</span>`
184
+ }
185
+ return html`<span class="badge badge-info" title=${`Publish by ${formattedDeadline}`}>${daysLeft}d remaining</span>`
186
+ }
187
+
130
188
  // ── Toast Container (imperative, lives outside Preact tree) ──
131
189
  const toastContainer = document.createElement('div')
132
190
  toastContainer.className = 'fixed top-4 right-4 z-50 flex flex-col gap-2'
@@ -288,7 +346,29 @@
288
346
  </div>`
289
347
  }
290
348
 
291
- const mediaUrl = `/media/queue/${encodeURIComponent(firstItem.id)}/media.mp4`
349
+ const mediaType = group.mediaType || 'video'
350
+ const mediaExt = mediaType === 'image' ? 'png' : 'mp4'
351
+ const mediaUrl = `/media/queue/${encodeURIComponent(firstItem.id)}/media.${mediaExt}`
352
+
353
+ if (mediaType === 'image') {
354
+ return html`
355
+ <div class="px-5 pb-3">
356
+ <div class="rounded-xl overflow-hidden bg-black/40 aspect-square relative">
357
+ <img
358
+ src=${mediaUrl}
359
+ alt="Generated cover image"
360
+ class="w-full h-full object-cover"
361
+ />
362
+ <div class="absolute top-3 left-3">
363
+ <span class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-purple-500/20 border border-purple-500/30">
364
+ <span class="text-sm">🎨</span>
365
+ <span class="text-xs font-semibold text-purple-300">Generated Image</span>
366
+ </span>
367
+ </div>
368
+ </div>
369
+ </div>`
370
+ }
371
+
292
372
  return html`
293
373
  <div class="px-5 pb-3">
294
374
  <div class="rounded-xl overflow-hidden bg-black/40 aspect-video relative">
@@ -397,13 +477,21 @@
397
477
 
398
478
  function GroupedPostCard({ group, accounts, selectedPlatforms, onTogglePlatform, animClass, onReject, onSkip, onApprove }) {
399
479
  const selectedCount = selectedPlatforms.length
480
+ const groupTitle = getGroupTitle(group)
481
+ const groupPlatforms = getGroupPlatforms(group)
482
+ const ideaPublishBy = getGroupIdeaPublishBy(group)
400
483
  return html`
401
484
  <div class="w-full max-w-2xl" style="max-height: calc(100vh - 160px); display: flex; flex-direction: column;">
402
485
  <div class="bg-card rounded-2xl shadow-2xl border border-white/5 overflow-hidden flex flex-col ${animClass}" style="max-height: 100%;" key=${group.groupKey}>
403
486
  <!-- Fixed top: clip type badge -->
404
- <div class="flex-shrink-0 flex items-center justify-between px-5 pt-4 pb-2">
405
- <div class="flex items-center gap-2">
406
- <${ClipTypeBadge} clipType=${group.clipType} />
487
+ <div class="flex-shrink-0 flex items-start justify-between px-5 pt-4 pb-2 gap-3">
488
+ <div class="min-w-0">
489
+ <div class="flex items-center gap-2 flex-wrap">
490
+ <${ClipTypeBadge} clipType=${group.clipType} />
491
+ <span class="text-sm font-semibold text-gray-200">${truncate(groupTitle, 48)}</span>
492
+ <${TimelinessBadge} publishBy=${ideaPublishBy} />
493
+ </div>
494
+ <div class="mt-1 text-xs text-gray-500">${groupPlatforms.join(' · ')}</div>
407
495
  </div>
408
496
  </div>
409
497
  <!-- Scrollable middle: video + checkboxes + preview -->
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "vidpipe",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "AI-powered pipeline that watches for video recordings and generates transcripts, summaries, short clips, and social media posts",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
5
+ "main": "dist/cli.js",
6
+ "types": "dist/cli.d.ts",
7
7
  "bin": {
8
- "vidpipe": "./dist/index.js"
8
+ "vidpipe": "./dist/cli.js"
9
9
  },
10
10
  "files": [
11
11
  "dist",
@@ -15,15 +15,26 @@
15
15
  "LICENSE"
16
16
  ],
17
17
  "scripts": {
18
- "start": "tsx src/index.ts",
18
+ "start": "tsx src/L7-app/cli.ts",
19
19
  "build": "tsup",
20
20
  "typecheck": "tsc --noEmit",
21
- "dev": "tsx watch src/index.ts",
21
+ "dev": "tsx watch src/L7-app/cli.ts",
22
22
  "test": "vitest run",
23
23
  "test:watch": "vitest",
24
24
  "test:coverage": "vitest run --coverage",
25
- "test:integration": "vitest run src/__tests__/integration/",
26
- "test:unit": "vitest run --exclude 'src/__tests__/integration/**'",
25
+ "test:unit": "vitest run --project unit",
26
+ "test:unit:coverage": "vitest run --project unit --coverage",
27
+ "test:integration": "vitest run --project integration-L3 --project integration-L4-L6 --project integration-L7",
28
+ "test:integration:coverage": "vitest run --project integration-L3 --project integration-L4-L6 --project integration-L7 --coverage",
29
+ "test:integration:L3": "vitest run --project integration-L3",
30
+ "test:integration:L3:coverage": "vitest run --project integration-L3 --coverage",
31
+ "test:integration:L4-L6": "vitest run --project integration-L4-L6",
32
+ "test:integration:L4-L6:coverage": "vitest run --project integration-L4-L6 --coverage",
33
+ "test:integration:L7": "vitest run --project integration-L7",
34
+ "test:integration:L7:coverage": "vitest run --project integration-L7 --coverage",
35
+ "test:e2e": "vitest run --project e2e",
36
+ "test:e2e:coverage": "vitest run --project e2e --coverage",
37
+ "commit": "tsx cicd/commit.ts",
27
38
  "push": "tsx cicd/push.ts"
28
39
  },
29
40
  "keywords": [