opencode-island 0.1.0 → 0.2.0
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/README.md +1 -1
- package/dist/gui-experimental.cjs +26 -2
- package/dist/island/index.html +10 -10
- package/package.json +1 -1
- package/src/gui.tsx +3 -0
- package/src/island/app/components/island.tsx +20 -16
- package/src/island/app/components/ui/toggle-group.tsx +0 -83
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-island",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A macOS Dynamic Island for OpenCode: live status of every session and agent on your machine, plus permission prompts you can answer without leaving your work.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode",
|
package/src/gui.tsx
CHANGED
|
@@ -32,6 +32,9 @@ export default Plugin.define({
|
|
|
32
32
|
},
|
|
33
33
|
onBlur: () => {
|
|
34
34
|
if (!open) return
|
|
35
|
+
// An island that is asking for an answer stays up: the window never
|
|
36
|
+
// has focus, so the app taking focus back would close it mid-question.
|
|
37
|
+
if (state.sessions.some((session) => session.permission)) return
|
|
35
38
|
open = false
|
|
36
39
|
island.send(message())
|
|
37
40
|
},
|
|
@@ -16,13 +16,19 @@ import {
|
|
|
16
16
|
import type { IslandMessage, PermissionDecision, SessionView } from "../../../state"
|
|
17
17
|
import { Button } from "@/components/ui/button"
|
|
18
18
|
import { Toggle } from "@/components/ui/toggle"
|
|
19
|
-
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
|
|
20
19
|
import { cn } from "@/lib/utils"
|
|
21
20
|
|
|
22
21
|
const EMPTY: IslandMessage = { sessions: [], working: 0, waiting: 0, open: false }
|
|
23
22
|
|
|
24
23
|
type Theme = "light" | "dark" | "system"
|
|
25
24
|
|
|
25
|
+
/** The appearance button cycles through the three, one click each. */
|
|
26
|
+
const THEMES: Theme[] = ["light", "dark", "system"]
|
|
27
|
+
|
|
28
|
+
function nextTheme(theme: Theme): Theme {
|
|
29
|
+
return THEMES[(THEMES.indexOf(theme) + 1) % THEMES.length]
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
function elapsed(ms: number) {
|
|
27
33
|
if (!ms || ms < 0) return ""
|
|
28
34
|
const seconds = Math.floor(ms / 1000)
|
|
@@ -357,26 +363,22 @@ export function Island() {
|
|
|
357
363
|
) : null}
|
|
358
364
|
|
|
359
365
|
<div className="flex items-center gap-1 border-t px-2 py-1.5">
|
|
360
|
-
<
|
|
361
|
-
type="single"
|
|
362
|
-
size="sm"
|
|
366
|
+
<Button
|
|
363
367
|
variant="outline"
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}}
|
|
368
|
-
|
|
368
|
+
size="icon-sm"
|
|
369
|
+
onClick={() => setTheme(nextTheme(theme))}
|
|
370
|
+
aria-label={`Appearance: ${theme}. Click for ${nextTheme(theme)}.`}
|
|
371
|
+
title={`Appearance: ${theme}`}
|
|
372
|
+
className={cn(alert && "border-warning-foreground/30 bg-transparent hover:bg-warning-foreground/10")}
|
|
369
373
|
>
|
|
370
|
-
|
|
374
|
+
{theme === "light" ? (
|
|
371
375
|
<Sun weight="duotone" />
|
|
372
|
-
|
|
373
|
-
<ToggleGroupItem value="dark" aria-label="Dark" title="Dark">
|
|
376
|
+
) : theme === "dark" ? (
|
|
374
377
|
<Moon weight="duotone" />
|
|
375
|
-
|
|
376
|
-
<ToggleGroupItem value="system" aria-label="System" title="System">
|
|
378
|
+
) : (
|
|
377
379
|
<Monitor weight="duotone" />
|
|
378
|
-
|
|
379
|
-
</
|
|
380
|
+
)}
|
|
381
|
+
</Button>
|
|
380
382
|
|
|
381
383
|
<span className="flex-1" />
|
|
382
384
|
|
|
@@ -387,6 +389,7 @@ export function Island() {
|
|
|
387
389
|
onPressedChange={setClickThrough}
|
|
388
390
|
aria-label="Click through"
|
|
389
391
|
title="Click through"
|
|
392
|
+
className={cn(alert && "border-warning-foreground/30 bg-transparent hover:bg-warning-foreground/10")}
|
|
390
393
|
>
|
|
391
394
|
<CursorClick weight="duotone" />
|
|
392
395
|
</Toggle>
|
|
@@ -397,6 +400,7 @@ export function Island() {
|
|
|
397
400
|
onPressedChange={setShowLatest}
|
|
398
401
|
aria-label="Show latest work"
|
|
399
402
|
title="Show latest work"
|
|
403
|
+
className={cn(alert && "border-warning-foreground/30 bg-transparent hover:bg-warning-foreground/10")}
|
|
400
404
|
>
|
|
401
405
|
<ListDashes weight="duotone" />
|
|
402
406
|
</Toggle>
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { type VariantProps } from "class-variance-authority"
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
6
|
-
import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui"
|
|
7
|
-
|
|
8
|
-
import { toggleVariants } from "@/components/ui/toggle"
|
|
9
|
-
|
|
10
|
-
const ToggleGroupContext = React.createContext<
|
|
11
|
-
VariantProps<typeof toggleVariants> & {
|
|
12
|
-
spacing?: number
|
|
13
|
-
}
|
|
14
|
-
>({
|
|
15
|
-
size: "default",
|
|
16
|
-
variant: "default",
|
|
17
|
-
spacing: 0,
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
function ToggleGroup({
|
|
21
|
-
className,
|
|
22
|
-
variant,
|
|
23
|
-
size,
|
|
24
|
-
spacing = 0,
|
|
25
|
-
children,
|
|
26
|
-
...props
|
|
27
|
-
}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
|
|
28
|
-
VariantProps<typeof toggleVariants> & {
|
|
29
|
-
spacing?: number
|
|
30
|
-
}) {
|
|
31
|
-
return (
|
|
32
|
-
<ToggleGroupPrimitive.Root
|
|
33
|
-
data-slot="toggle-group"
|
|
34
|
-
data-variant={variant}
|
|
35
|
-
data-size={size}
|
|
36
|
-
data-spacing={spacing}
|
|
37
|
-
style={{ "--gap": spacing } as React.CSSProperties}
|
|
38
|
-
className={cn(
|
|
39
|
-
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=default]:data-[variant=outline]:shadow-xs",
|
|
40
|
-
className
|
|
41
|
-
)}
|
|
42
|
-
{...props}
|
|
43
|
-
>
|
|
44
|
-
<ToggleGroupContext.Provider value={{ variant, size, spacing }}>
|
|
45
|
-
{children}
|
|
46
|
-
</ToggleGroupContext.Provider>
|
|
47
|
-
</ToggleGroupPrimitive.Root>
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function ToggleGroupItem({
|
|
52
|
-
className,
|
|
53
|
-
children,
|
|
54
|
-
variant,
|
|
55
|
-
size,
|
|
56
|
-
...props
|
|
57
|
-
}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
|
|
58
|
-
VariantProps<typeof toggleVariants>) {
|
|
59
|
-
const context = React.useContext(ToggleGroupContext)
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<ToggleGroupPrimitive.Item
|
|
63
|
-
data-slot="toggle-group-item"
|
|
64
|
-
data-variant={context.variant || variant}
|
|
65
|
-
data-size={context.size || size}
|
|
66
|
-
data-spacing={context.spacing}
|
|
67
|
-
className={cn(
|
|
68
|
-
toggleVariants({
|
|
69
|
-
variant: context.variant || variant,
|
|
70
|
-
size: context.size || size,
|
|
71
|
-
}),
|
|
72
|
-
"w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10",
|
|
73
|
-
"data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",
|
|
74
|
-
className
|
|
75
|
-
)}
|
|
76
|
-
{...props}
|
|
77
|
-
>
|
|
78
|
-
{children}
|
|
79
|
-
</ToggleGroupPrimitive.Item>
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export { ToggleGroup, ToggleGroupItem }
|