Compare commits

..

7 Commits

Author SHA1 Message Date
viper151
051a6b1e74 chore(release): v1.27.1 2026-03-29 01:15:38 +00:00
simosmik
f1063fd339 chore: release tokens 2026-03-29 01:13:13 +00:00
simosmik
27cd12432b chore: relicense to AGPL-3.0-or-later
Siteboon AI B.V. contributions relicensed from GPL-3.0 to
AGPL-3.0-or-later. Existing community contributions remain
under GPL-3.0, combined per GPL-3.0 Section 13.
Adds Section 7 additional terms (attribution, origin
protection, publicity restriction, trademark).
2026-03-29 00:57:09 +00:00
simosmik
004135ef01 chore: add terminal plugin in the plugins list
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 10:38:00 +00:00
xiguatoutou
b54cdf8168 fix: prevent split on undefined(#491) (#563) 2026-03-23 20:14:15 +03:00
simosmik
42a131389a chore: add release-it github action 2026-03-22 01:41:21 +00:00
simosmik
ebd1c0db92 chore(release): v1.26.3 2026-03-22 01:10:13 +00:00
11 changed files with 625 additions and 413 deletions

50
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Release
on:
workflow_dispatch:
inputs:
increment:
description: 'Version bump: patch, minor, major, or explicit (e.g. 1.27.0)'
required: true
default: 'patch'
type: string
release_name:
description: 'Custom release name (optional, defaults to "CloudCLI UI vX.Y.Z")'
required: false
type: string
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.RELEASE_PAT }}
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org
- name: git config
run: |
git config user.name "${GITHUB_ACTOR}"
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
- run: npm ci
- name: Release
run: |
ARGS="--ci --increment=${{ inputs.increment }}"
if [ -n "${{ inputs.release_name }}" ]; then
ARGS="$ARGS --github.releaseName=\"${{ inputs.release_name }}\""
fi
npx release-it $ARGS
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -3,6 +3,21 @@
All notable changes to CloudCLI UI will be documented in this file. All notable changes to CloudCLI UI will be documented in this file.
## [1.27.1](https://github.com/siteboon/claudecodeui/compare/v1.26.3...v1.27.1) (2026-03-29)
### Bug Fixes
* prevent split on undefined[#491](https://github.com/siteboon/claudecodeui/issues/491) ([#563](https://github.com/siteboon/claudecodeui/issues/563)) ([b54cdf8](https://github.com/siteboon/claudecodeui/commit/b54cdf8168fc224e9907796e4229ae8ed34e6885))
### Maintenance
* add release-it github action ([42a1313](https://github.com/siteboon/claudecodeui/commit/42a131389a6954df0d2c3bedd2cb6d3406c5ebc1))
* add terminal plugin in the plugins list ([004135e](https://github.com/siteboon/claudecodeui/commit/004135ef0187023e1da29c4a7137a28a42ebf9af))
* release tokens ([f1063fd](https://github.com/siteboon/claudecodeui/commit/f1063fd33964ccb517f5ebcdd14526ed162e1138))
* relicense to AGPL-3.0-or-later ([27cd124](https://github.com/siteboon/claudecodeui/commit/27cd12432b7d3237981f86acd9cc99532d843d4a))
## [1.26.3](https://github.com/siteboon/claudecodeui/compare/v1.26.2...v1.26.3) (2026-03-22)
## [1.26.2](https://github.com/siteboon/claudecodeui/compare/v1.26.0...v1.26.2) (2026-03-21) ## [1.26.2](https://github.com/siteboon/claudecodeui/compare/v1.26.0...v1.26.2) (2026-03-21)
### Bug Fixes ### Bug Fixes

View File

@@ -153,4 +153,4 @@ This automatically:
## License ## License
By contributing, you agree that your contributions will be licensed under the [GPL-3.0 License](LICENSE). By contributing, you agree that your contributions will be licensed under the [AGPL-3.0-or-later License](LICENSE), including the additional terms specified in Section 7 of the LICENSE file.

789
LICENSE

File diff suppressed because it is too large Load Diff

13
NOTICE Normal file
View File

@@ -0,0 +1,13 @@
CloudCLI UI
Copyright 2025-2026 Siteboon AI B.V. and contributors
This software is licensed under the GNU Affero General Public License v3.0
or later (AGPL-3.0-or-later). See the LICENSE file for the full license text,
including additional terms under Section 7.
Originally developed by Siteboon AI B.V. (https://github.com/siteboon/claudecodeui).
Contributions by Siteboon AI B.V. prior to commit 004135ef were originally
published under GPL-3.0 and are hereby relicensed to AGPL-3.0-or-later.
Contributions by other authors prior to that commit remain under GPL-3.0
and are incorporated into this work as permitted by GPL-3.0 Section 13.

View File

@@ -213,9 +213,11 @@ Yes, for self-hosted. CloudCLI UI reads from and writes to the same `~/.claude`
## License ## License
GNU General Public License v3.0 - see [LICENSE](LICENSE) file for details. GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later) — see [LICENSE](LICENSE) for the full text, including additional terms under Section 7.
This project is open source and free to use, modify, and distribute under the GPL v3 license. This project is open source and free to use, modify, and distribute under the AGPL-3.0-or-later license. If you modify this software and run it as a network service, you must make your modified source code available to users of that service.
CloudCLI UI - (https://cloudcli.ai).
## Acknowledgments ## Acknowledgments

6
package-lock.json generated
View File

@@ -1,14 +1,14 @@
{ {
"name": "@siteboon/claude-code-ui", "name": "@siteboon/claude-code-ui",
"version": "1.26.2", "version": "1.27.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@siteboon/claude-code-ui", "name": "@siteboon/claude-code-ui",
"version": "1.26.2", "version": "1.27.1",
"hasInstallScript": true, "hasInstallScript": true,
"license": "GPL-3.0", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.59", "@anthropic-ai/claude-agent-sdk": "^0.2.59",
"@codemirror/lang-css": "^6.3.1", "@codemirror/lang-css": "^6.3.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@siteboon/claude-code-ui", "name": "@siteboon/claude-code-ui",
"version": "1.26.2", "version": "1.27.1",
"description": "A web-based UI for Claude Code CLI", "description": "A web-based UI for Claude Code CLI",
"type": "module", "type": "module",
"main": "server/index.js", "main": "server/index.js",
@@ -46,7 +46,7 @@
"mobile" "mobile"
], ],
"author": "CloudCLI UI Contributors", "author": "CloudCLI UI Contributors",
"license": "GPL-3.0", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.59", "@anthropic-ai/claude-agent-sdk": "^0.2.59",
"@codemirror/lang-css": "^6.3.1", "@codemirror/lang-css": "^6.3.1",

View File

@@ -33,7 +33,12 @@ export const ToolDiffViewer: React.FC<ToolDiffViewerProps> = ({
: 'bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400'; : 'bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400';
const diffLines = useMemo( const diffLines = useMemo(
() => createDiff(oldContent, newContent), () => {
if (oldContent === undefined || newContent === undefined) {
return [];
}
return createDiff(oldContent, newContent)
},
[createDiff, oldContent, newContent] [createDiff, oldContent, newContent]
); );

View File

@@ -6,6 +6,7 @@ import type { Plugin } from '../../../contexts/PluginsContext';
import PluginIcon from './PluginIcon'; import PluginIcon from './PluginIcon';
const STARTER_PLUGIN_URL = 'https://github.com/cloudcli-ai/cloudcli-plugin-starter'; const STARTER_PLUGIN_URL = 'https://github.com/cloudcli-ai/cloudcli-plugin-starter';
const TERMINAL_PLUGIN_URL = 'https://github.com/cloudcli-ai/cloudcli-plugin-terminal';
/* ─── Toggle Switch ─────────────────────────────────────────────────────── */ /* ─── Toggle Switch ─────────────────────────────────────────────────────── */
function ToggleSwitch({ checked, onChange, ariaLabel }: { checked: boolean; onChange: (v: boolean) => void; ariaLabel: string }) { function ToggleSwitch({ checked, onChange, ariaLabel }: { checked: boolean; onChange: (v: boolean) => void; ariaLabel: string }) {
@@ -264,6 +265,67 @@ function StarterPluginCard({ onInstall, installing }: { onInstall: () => void; i
); );
} }
/* ─── Terminal Plugin Card ──────────────────────────────────────────────── */
function TerminalPluginCard({ onInstall, installing }: { onInstall: () => void; installing: boolean }) {
const { t } = useTranslation('settings');
return (
<div className="relative flex overflow-hidden rounded-lg border border-dashed border-border bg-card transition-all duration-200 hover:border-blue-400 dark:hover:border-blue-500">
<div className="w-[3px] flex-shrink-0 bg-blue-500/30" />
<div className="min-w-0 flex-1 p-4">
<div className="flex items-start justify-between gap-3">
<div className="flex min-w-0 items-center gap-2.5">
<div className="h-5 w-5 flex-shrink-0 text-blue-500">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="h-5 w-5">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M7 8l4 4-4 4"/>
<line x1="13" y1="16" x2="17" y2="16"/>
</svg>
</div>
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-2">
<span className="text-sm font-semibold leading-none text-foreground">
{t('pluginSettings.terminalPlugin.name')}
</span>
<span className="rounded bg-blue-50 px-1.5 py-0.5 text-[10px] font-medium text-blue-600 dark:bg-blue-950/50 dark:text-blue-400">
{t('pluginSettings.terminalPlugin.badge')}
</span>
<span className="rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground">
{t('pluginSettings.tab')}
</span>
</div>
<p className="mt-1 text-sm leading-snug text-muted-foreground">
{t('pluginSettings.terminalPlugin.description')}
</p>
<a
href={TERMINAL_PLUGIN_URL}
target="_blank"
rel="noopener noreferrer"
className="mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground/60 transition-colors hover:text-foreground"
>
<GitBranch className="h-3 w-3" />
cloudcli-ai/cloudcli-plugin-terminal
</a>
</div>
</div>
<button
onClick={onInstall}
disabled={installing}
className="flex flex-shrink-0 items-center gap-1.5 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:opacity-50"
>
{installing ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<Download className="h-3.5 w-3.5" />
)}
{installing ? t('pluginSettings.installing') : t('pluginSettings.terminalPlugin.install')}
</button>
</div>
</div>
</div>
);
}
/* ─── Main Component ────────────────────────────────────────────────────── */ /* ─── Main Component ────────────────────────────────────────────────────── */
export default function PluginSettingsTab() { export default function PluginSettingsTab() {
const { t } = useTranslation('settings'); const { t } = useTranslation('settings');
@@ -273,6 +335,7 @@ export default function PluginSettingsTab() {
const [gitUrl, setGitUrl] = useState(''); const [gitUrl, setGitUrl] = useState('');
const [installing, setInstalling] = useState(false); const [installing, setInstalling] = useState(false);
const [installingStarter, setInstallingStarter] = useState(false); const [installingStarter, setInstallingStarter] = useState(false);
const [installingTerminal, setInstallingTerminal] = useState(false);
const [installError, setInstallError] = useState<string | null>(null); const [installError, setInstallError] = useState<string | null>(null);
const [confirmUninstall, setConfirmUninstall] = useState<string | null>(null); const [confirmUninstall, setConfirmUninstall] = useState<string | null>(null);
const [updatingPlugins, setUpdatingPlugins] = useState<Set<string>>(new Set()); const [updatingPlugins, setUpdatingPlugins] = useState<Set<string>>(new Set());
@@ -311,6 +374,16 @@ export default function PluginSettingsTab() {
setInstallingStarter(false); setInstallingStarter(false);
}; };
const handleInstallTerminal = async () => {
setInstallingTerminal(true);
setInstallError(null);
const result = await installPlugin(TERMINAL_PLUGIN_URL);
if (!result.success) {
setInstallError(result.error || t('pluginSettings.installFailed'));
}
setInstallingTerminal(false);
};
const handleUninstall = async (name: string) => { const handleUninstall = async (name: string) => {
if (confirmUninstall !== name) { if (confirmUninstall !== name) {
setConfirmUninstall(name); setConfirmUninstall(name);
@@ -326,6 +399,7 @@ export default function PluginSettingsTab() {
}; };
const hasStarterInstalled = plugins.some((p) => p.name === 'project-stats'); const hasStarterInstalled = plugins.some((p) => p.name === 'project-stats');
const hasTerminalInstalled = plugins.some((p) => p.name === 'web-terminal');
return ( return (
<div className="space-y-6"> <div className="space-y-6">
@@ -382,9 +456,16 @@ export default function PluginSettingsTab() {
</span> </span>
</p> </p>
{/* Starter plugin suggestion — above the list */} {/* Official plugin suggestions — above the list */}
{!loading && !hasStarterInstalled && ( {!loading && (!hasStarterInstalled || !hasTerminalInstalled) && (
<StarterPluginCard onInstall={handleInstallStarter} installing={installingStarter} /> <div className="space-y-2">
{!hasStarterInstalled && (
<StarterPluginCard onInstall={handleInstallStarter} installing={installingStarter} />
)}
{!hasTerminalInstalled && (
<TerminalPluginCard onInstall={handleInstallTerminal} installing={installingTerminal} />
)}
</div>
)} )}
{/* Plugin List */} {/* Plugin List */}
@@ -423,33 +504,30 @@ export default function PluginSettingsTab() {
</div> </div>
)} )}
{/* Build your own */} {/* Starter plugin */}
<div className="flex items-center justify-between gap-4 border-t border-border/50 pt-2"> <div className="flex items-center justify-center gap-3 border-t border-border/50 pt-2">
<div className="flex min-w-0 items-center gap-2"> <BookOpen className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground/40" />
<BookOpen className="h-3.5 w-3.5 flex-shrink-0 text-muted-foreground/40" /> <span className="text-xs text-muted-foreground/60">
<span className="text-xs text-muted-foreground/60"> {t('pluginSettings.starterPluginLabel')}
{t('pluginSettings.buildYourOwn')} </span>
</span> <span className="text-muted-foreground/20">·</span>
</div> <a
<div className="flex flex-shrink-0 items-center gap-3"> href={STARTER_PLUGIN_URL}
<a target="_blank"
href={STARTER_PLUGIN_URL} rel="noopener noreferrer"
target="_blank" className="inline-flex items-center gap-1 text-xs text-muted-foreground/60 transition-colors hover:text-foreground"
rel="noopener noreferrer" >
className="inline-flex items-center gap-1 text-xs text-muted-foreground/60 transition-colors hover:text-foreground" {t('pluginSettings.starter')} <ExternalLink className="h-2.5 w-2.5" />
> </a>
{t('pluginSettings.starter')} <ExternalLink className="h-2.5 w-2.5" /> <span className="text-muted-foreground/20">·</span>
</a> <a
<span className="text-muted-foreground/20">·</span> href="https://cloudcli.ai/docs/plugin-overview"
<a target="_blank"
href="https://cloudcli.ai/docs/plugin-overview" rel="noopener noreferrer"
target="_blank" className="inline-flex items-center gap-1 text-xs text-muted-foreground/60 transition-colors hover:text-foreground"
rel="noopener noreferrer" >
className="inline-flex items-center gap-1 text-xs text-muted-foreground/60 transition-colors hover:text-foreground" {t('pluginSettings.docs')} <ExternalLink className="h-2.5 w-2.5" />
> </a>
{t('pluginSettings.docs')} <ExternalLink className="h-2.5 w-2.5" />
</a>
</div>
</div> </div>
</div> </div>
); );

View File

@@ -476,7 +476,7 @@
"installFailed": "Installation failed", "installFailed": "Installation failed",
"uninstallFailed": "Uninstall failed", "uninstallFailed": "Uninstall failed",
"toggleFailed": "Toggle failed", "toggleFailed": "Toggle failed",
"buildYourOwn": "Build your own plugin", "starterPluginLabel": "Starter Plugin",
"starter": "Starter", "starter": "Starter",
"docs": "Docs", "docs": "Docs",
"starterPlugin": { "starterPlugin": {
@@ -485,6 +485,12 @@
"description": "File counts, lines of code, file-type breakdown, and recent activity for your project.", "description": "File counts, lines of code, file-type breakdown, and recent activity for your project.",
"install": "Install" "install": "Install"
}, },
"terminalPlugin": {
"name": "Terminal",
"badge": "official",
"description": "Integrated terminal with full shell access directly within the interface.",
"install": "Install"
},
"morePlugins": "More", "morePlugins": "More",
"enable": "Enable", "enable": "Enable",
"disable": "Disable", "disable": "Disable",