opencode-quotes-plugin 1.1.0 → 1.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.
Files changed (4) hide show
  1. package/README.md +6 -4
  2. package/package.json +26 -1
  3. package/quotes.ts +1 -1
  4. package/tui.tsx +88 -31
package/README.md CHANGED
@@ -8,10 +8,12 @@ A motivational quotes plugin for OpenCode.
8
8
 
9
9
  ## Installation
10
10
 
11
- 1. In OpenCode, press control + P to bring up the command prompt
12
- 2. Search and select "Install Plugin"
13
- 3. Enter `opencode-quotes-plugin`
14
- 4. Restart OpenCode
11
+ Run:
12
+ ```bash
13
+ opencode plugin opencode-quotes-plugin -g
14
+ ```
15
+
16
+ This will install the plugin globally.
15
17
 
16
18
  ## Usage
17
19
 
package/package.json CHANGED
@@ -1,6 +1,27 @@
1
1
  {
2
2
  "name": "opencode-quotes-plugin",
3
- "version": "1.1.0",
3
+ "description": "A motivational quotes plugin for OpenCode",
4
+ "keywords": [
5
+ "opencode",
6
+ "opencode-cli",
7
+ "opencode-ai",
8
+ "opentui",
9
+ "plugin",
10
+ "motivational",
11
+ "inspirational",
12
+ "motivation",
13
+ "inspiration",
14
+ "quotes",
15
+ "opentui",
16
+ "openai",
17
+ "codex",
18
+ "anthropic",
19
+ "claude-code",
20
+ "claude",
21
+ "gemini",
22
+ "copilot"
23
+ ],
24
+ "version": "1.2.0",
4
25
  "type": "module",
5
26
  "exports": {
6
27
  "./tui": {
@@ -30,5 +51,9 @@
30
51
  "@types/bun": "latest",
31
52
  "solid-js": "^1.9.12",
32
53
  "typescript": "^5.9.3"
54
+ },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/aerovato/opencode-quotes-plugin"
33
58
  }
34
59
  }
package/quotes.ts CHANGED
@@ -22,7 +22,7 @@ export const QUOTES = [
22
22
  "Energy and persistence conquer all things. — Benjamin Franklin",
23
23
  "If you can't fly then run, if you can't run then walk, if you can't walk then crawl. But whatever you do, you have to keep moving forward. — Martin Luther King Jr.",
24
24
  "You just can't beat the person who never gives up. — Babe Ruth",
25
- "We choose to go to the moon ..., not because they are easy, but because they are hard, ..., because that challenge is one that we are willing to accept, one we are unwilling to postpone, and one which we intend to win, and the others, too. — John F. Kennedy",
25
+ "We choose to go to the moon ..., because that challenge is one that we are willing to accept, one we are unwilling to postpone, and one which we intend to win, and the others, too. — John F. Kennedy",
26
26
  "Failure is simply the opportunity to begin again, this time more intelligently. — Henry Ford",
27
27
  "The impediment to action advances action. What stands in the way becomes the way. — Marcus Aurelius",
28
28
  "Difficulties strengthen the mind, as labor does the body. — Seneca",
package/tui.tsx CHANGED
@@ -1,46 +1,103 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
2
 
3
- import type { TuiPlugin, TuiPluginModule, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
4
- import { createMemo, Show } from "solid-js"
3
+ import type {
4
+ TuiPlugin,
5
+ TuiPluginModule,
6
+ TuiThemeCurrent,
7
+ } from "@opencode-ai/plugin/tui";
8
+ import { createMemo, For, Show } from "solid-js";
5
9
 
6
- import { QUOTES } from "./quotes"
10
+ import { QUOTES } from "./quotes";
11
+ import { TextAttributes } from "@opentui/core";
12
+ import { useTerminalDimensions } from "@opentui/solid";
7
13
 
8
- function Tips(props: { theme: TuiThemeCurrent }) {
14
+ const MAX_QUOTES_WIDTH = 66;
15
+
16
+ function wordWrap(text: string, maxWidth: number): string[] {
17
+ const words = text.split(" ");
18
+ if (words.length === 0) {
19
+ return [];
20
+ }
21
+ const totalLen = text.length;
22
+ const targetCharsPerLine = Math.ceil(
23
+ totalLen / Math.ceil(totalLen / maxWidth),
24
+ );
25
+ const lines: string[] = [];
26
+ let wordIndex = 0;
27
+ let accumulatedWidth = 0;
28
+
29
+ while (wordIndex < words.length) {
30
+ let capacity = Math.min(
31
+ targetCharsPerLine + accumulatedWidth + (wordIndex === 0 ? 4 : 0),
32
+ maxWidth,
33
+ );
34
+ let line = words[wordIndex]!;
35
+ wordIndex++;
36
+ while (wordIndex < words.length) {
37
+ const test = line + " " + words[wordIndex]!;
38
+ if (test.length <= capacity) {
39
+ line = test;
40
+ wordIndex++;
41
+ } else {
42
+ break;
43
+ }
44
+ }
45
+ lines.push(line);
46
+ accumulatedWidth = capacity - line.length;
47
+ }
48
+
49
+ return lines;
50
+ }
51
+
52
+ function Quotes(props: { theme: TuiThemeCurrent }) {
9
53
  const quote = QUOTES[Math.floor(Math.random() * QUOTES.length)]!;
10
- const split = quote.split("—"); // NOTE: Emdash; regular dashes are for names.
11
- const author = split.at(-1)?.trim();
54
+ const split = quote.split("—");
55
+ const author = split.at(-1)?.trim() || "";
12
56
  const text = split.slice(0, -1).join("—").trim();
13
- if (author === undefined || text === undefined) {
14
- throw new Error(`Error: Malformed quote ${quote}.`)
15
- }
57
+
58
+ const dimensions = useTerminalDimensions();
59
+ const lines = createMemo(() =>
60
+ wordWrap(text, Math.min(MAX_QUOTES_WIDTH, dimensions().width - 8)),
61
+ );
16
62
 
17
63
  return (
18
- <box maxWidth="100%" flexDirection="column" paddingX={6}>
19
- <text alignItems="center" style={{ fg: props.theme.text }}>
20
- {text}
21
- </text>
64
+ <box width="100%" flexDirection="column">
65
+ <For each={lines()}>
66
+ {line => (
67
+ <text alignSelf="center" style={{ fg: props.theme.text }}>
68
+ {line}
69
+ </text>
70
+ )}
71
+ </For>
22
72
  <text
23
- alignSelf={text.length < 40 ? "center" : "flex-end"}
24
- style={{ fg: props.theme.primary }}
73
+ alignSelf={"center"}
74
+ attributes={TextAttributes.DIM}
75
+ style={{ fg: props.theme.accent }}
25
76
  >
26
- - {author}
77
+ <em>- {author}</em>
27
78
  </text>
28
79
  </box>
29
- )
80
+ );
30
81
  }
31
82
 
32
83
  function View(props: { show: boolean; theme: TuiThemeCurrent }) {
33
84
  return (
34
- <box minHeight={0} width="100%" maxWidth={75} alignItems="center" paddingY={2} flexShrink={1}>
85
+ <box
86
+ minHeight={4}
87
+ width="100%"
88
+ maxWidth={MAX_QUOTES_WIDTH}
89
+ alignItems="center"
90
+ paddingY={2}
91
+ >
35
92
  <Show when={props.show}>
36
- <Tips theme={props.theme} />
93
+ <Quotes theme={props.theme} />
37
94
  </Show>
38
95
  </box>
39
- )
96
+ );
40
97
  }
41
98
 
42
- const tui: TuiPlugin = async (api) => {
43
- api.plugins.deactivate("internal:home-tips")
99
+ const tui: TuiPlugin = async api => {
100
+ api.plugins.deactivate("internal:home-tips");
44
101
 
45
102
  api.command.register(() => [
46
103
  {
@@ -50,22 +107,22 @@ const tui: TuiPlugin = async (api) => {
50
107
  category: "System",
51
108
  hidden: api.route.current.name !== "home",
52
109
  onSelect() {
53
- api.kv.set("tips_hidden", !api.kv.get("tips_hidden", false))
54
- api.ui.dialog.clear()
110
+ api.kv.set("tips_hidden", !api.kv.get("tips_hidden", false));
111
+ api.ui.dialog.clear();
55
112
  },
56
113
  },
57
- ])
114
+ ]);
58
115
 
59
116
  api.slots.register({
60
117
  order: 100,
61
118
  slots: {
62
119
  home_bottom() {
63
- const hidden = createMemo(() => api.kv.get("tips_hidden", false))
64
- const show = createMemo(() => !hidden())
65
- return <View show={show()} theme={api.theme.current} />
120
+ const hidden = createMemo(() => api.kv.get("tips_hidden", false));
121
+ const show = createMemo(() => !hidden());
122
+ return <View show={show()} theme={api.theme.current} />;
66
123
  },
67
124
  },
68
- })
69
- }
125
+ });
126
+ };
70
127
 
71
- export default { id: "opencode-quotes-plugin", tui } satisfies TuiPluginModule
128
+ export default { id: "opencode-quotes-plugin", tui } satisfies TuiPluginModule;