Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ai.codesetu.settings.CodeSetuModelCatalog
import ai.codesetu.settings.CodeSetuSettingsState
import ai.codesetu.settings.providerDefaults
import ai.codesetu.settings.resolveCodeSetuModel
import com.intellij.ide.BrowserUtil
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ReadAction
Expand Down Expand Up @@ -98,6 +99,11 @@ class CodeSetuChatPanel(private val project: Project) : Disposable {
when (obj["type"]?.jsonPrimitive?.contentOrNull) {
"ready" -> onReady()
"selectModel" -> showModelPicker()
"configureProvider" -> ApplicationManager.getApplication().invokeLater { configureProvider() }
"openUrl" -> {
val url = obj["url"]?.jsonPrimitive?.contentOrNull ?: return
ApplicationManager.getApplication().invokeLater { BrowserUtil.browse(url) }
}
"sendMessage" -> {
val text = obj["text"]?.jsonPrimitive?.contentOrNull ?: return
val include = obj["includeIdeContext"]?.jsonPrimitive?.booleanOrNull ?: true
Expand Down Expand Up @@ -272,6 +278,7 @@ class CodeSetuChatPanel(private val project: Project) : Disposable {
CodeSetuSettingsState.getInstance().setApiKey(token)
}
pushModelLabel()
pushWelcome()
}

private fun pushModelLabel() {
Expand Down Expand Up @@ -303,9 +310,23 @@ class CodeSetuChatPanel(private val project: Project) : Disposable {
ready = true
pending.forEach { executeJs(it) }
pending.clear()
pushWelcome()
}
}

// The welcome panel shows on first use, when no provider key is configured.
// Once the user has a key (or sends a message), it stays hidden for the
// session.
private fun pushWelcome() {
push(message("welcome") { put("show", !isConfigured()) })
}

private fun isConfigured(): Boolean {
if (CodeSetuSettingsState.getInstance().getApiKey().isNotBlank()) return true
return sequenceOf("CODESETU_API_KEY", "SARVAM_API_KEY", "HF_TOKEN")
.any { !System.getenv(it).isNullOrBlank() }
}

private fun executeJs(json: String) {
browser.cefBrowser.executeJavaScript("window.__codesetuReceive($json)", browser.cefBrowser.url ?: "", 0)
}
Expand Down
55 changes: 50 additions & 5 deletions apps/jetbrains/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,56 @@
<vendor url="https://github.com/getcodesetu/codesetu">CodeSetu Maintainers</vendor>

<description><![CDATA[
Open-source AI coding assistant for JetBrains IDEs.
Multi-provider — works with Sarvam, OpenAI-compatible APIs (Ollama, vLLM, OpenRouter), and local self-hosted deployments.
Built for Indian developers, public-sector teams, and air-gapped enterprises.<br/><br/>
This plugin is in early scaffold. Source: <a href="https://github.com/getcodesetu/codesetu">github.com/getcodesetu/codesetu</a>
&middot; Community: <a href="https://discord.gg/sjVKU8cpC6">Discord</a>
<h2>CodeSetu — Open-source AI coding assistant for JetBrains IDEs</h2>

<p>
A privacy-aware, multi-provider Copilot / Cursor alternative for Indian developers,
enterprises, public-sector teams, and air-gapped deployments.
<b>Bring your own model.</b>
</p>

<h3>What you get</h3>
<ul>
<li><b>Chat tool window</b> — streamed, markdown-rendered replies with rendered code blocks, in a polished CodeSetu webview.</li>
<li><b>Selected-code actions</b> — Explain, Refactor, Write Tests, Fix Bug, Add Docs, right from the editor context menu.</li>
<li><b>Repo-aware context</b> — active file, selection, and cursor neighborhood are attached automatically (toggleable per message).</li>
<li><b>In-chat provider &amp; model switcher</b> — change providers, base URLs, and models without leaving the chat.</li>
<li><b>Workspace skills &amp; checks</b> — drop <code>.codesetu/skills/*.md</code> and <code>.codesetu/checks/*.md</code> files; they are folded into the system prompt.</li>
<li><b>Multi-turn conversation history</b> — kept per session.</li>
</ul>

<h3>Works with</h3>
<ul>
<li><b>Sarvam</b> — Indic-aware hosted models.</li>
<li><b>Hugging Face</b> — the hosted router (<code>router.huggingface.co/v1</code>), your dedicated Inference Endpoints, or self-hosted TGI. Any served chat model (Llama, Qwen, DeepSeek, Mistral, Gemma…) with your <code>hf_…</code> token.</li>
<li><b>OpenAI-compatible</b> — Ollama, vLLM, SGLang, OpenRouter, LM Studio, any compatible endpoint.</li>
<li><b>Self-hosted &amp; air-gapped</b> — bring your own URL. No outbound calls beyond the provider you configure.</li>
</ul>

<h3>Privacy &amp; security</h3>
<ul>
<li>API keys live in the OS <b>PasswordSafe</b> (keychain) — never in plaintext settings XML.</li>
<li>Common secret files (<code>.env*</code>, <code>*.pem</code>, <code>*.key</code>, <code>secrets/</code>, …) are excluded from auto-collected workspace context.</li>
<li>No CodeSetu telemetry. Your prompts go only to the provider you configured.</li>
<li>100% open-source under Apache 2.0.</li>
</ul>

<h3>Quick start</h3>
<ol>
<li>Install the plugin and restart the IDE.</li>
<li>Open the <b>CodeSetu</b> tool window (right dock).</li>
<li>Click the <b>provider · model</b> chip in the composer → <b>Configure provider / endpoint…</b> → pick Sarvam, Hugging Face, or OpenAI-compatible, paste your key, and start chatting.</li>
</ol>

<p><b>Selected-code actions:</b> right-click any selection → <i>Explain / Refactor / Write Tests / Fix Bug / Add Docs with CodeSetu</i>.</p>

<h3>Links</h3>
<ul>
<li><a href="https://github.com/getcodesetu/codesetu">Source on GitHub</a></li>
<li><a href="https://github.com/getcodesetu/codesetu/blob/main/INSTALL.md">Install &amp; setup guide</a></li>
<li><a href="https://discord.gg/sjVKU8cpC6">Community Discord</a></li>
<li><a href="https://github.com/getcodesetu/codesetu/issues">Report an issue</a></li>
</ul>
]]></description>

<change-notes><![CDATA[
Expand Down
196 changes: 196 additions & 0 deletions apps/jetbrains/src/main/resources/webview/chat.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,104 @@
border: 1px solid var(--vscode-inputValidation-errorBorder);
}

#welcome[hidden] {
display: none;
}

.welcome-card {
padding: 14px 16px;
border: 1px solid var(--vscode-input-border, var(--vscode-widget-border));
border-radius: 12px;
background: var(--vscode-editor-inactiveSelectionBackground);
}

.welcome-card h2 {
margin: 0 0 6px;
font-size: 16px;
line-height: 1.25;
}

.welcome-card h3 {
margin: 14px 0 6px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.6px;
text-transform: uppercase;
color: var(--vscode-descriptionForeground);
}

.welcome-card p {
margin: 0 0 10px;
color: var(--vscode-descriptionForeground);
}

.welcome-steps,
.welcome-bullets {
margin: 0 0 8px 18px;
padding: 0;
}

.welcome-steps li,
.welcome-bullets li {
margin: 4px 0;
}

.welcome-bullets b {
color: var(--vscode-foreground);
}

.welcome-bullets code {
font-family: var(--vscode-editor-font-family, monospace);
font-size: 0.92em;
padding: 1px 4px;
border-radius: 4px;
background: var(--vscode-textCodeBlock-background, rgba(127, 127, 127, 0.12));
}

.welcome-tip {
margin: 12px 0 0;
padding: 8px 10px;
border-radius: 6px;
background: var(--vscode-textCodeBlock-background, rgba(127, 127, 127, 0.08));
color: var(--vscode-foreground);
font-size: 0.95em;
}

.welcome-actions {
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
}

.welcome-actions .primary {
padding: 6px 12px;
border: 0;
border-radius: 7px;
cursor: pointer;
font: inherit;
color: var(--vscode-button-foreground);
background: var(--vscode-button-background);
}

.welcome-actions .primary:hover {
background: var(--vscode-button-hoverBackground);
}

.welcome-actions .link {
padding: 6px 4px;
border: 0;
background: transparent;
cursor: pointer;
font: inherit;
color: var(--vscode-descriptionForeground);
text-decoration: underline;
}

.welcome-actions .link:hover {
color: var(--vscode-foreground);
}

.composer-wrap {
position: relative;
display: grid;
Expand Down Expand Up @@ -392,6 +490,87 @@
<body>
<main>
<section id="transcript" aria-live="polite"></section>
<section id="welcome" hidden>
<div class="welcome-card">
<h2>Welcome to CodeSetu</h2>
<p>
A privacy-aware, open-source AI coding assistant.
<b>Bring your own model</b> — Sarvam, Hugging Face, or any OpenAI-compatible API
(Ollama, vLLM, local). Your key is stored in the OS keychain; your prompts go only to
the provider you configure.
</p>

<h3>Get started in 30 seconds</h3>
<ol class="welcome-steps">
<li>Click <b>Configure provider</b> below.</li>
<li>Pick a provider, paste your API key (stored in the OS keychain).</li>
<li>Type a question and hit send.</li>
</ol>

<div class="welcome-actions">
<button id="welcome-configure" type="button" class="primary">Configure provider</button>
<button id="welcome-docs" type="button" class="link">Learn more</button>
</div>

<h3>What you can do</h3>
<ul class="welcome-bullets">
<li>
<b>Repo-aware chat</b> — your active file and selection are attached automatically.
Toggle this off any time from the <b>+</b> menu in the composer.
</li>
<li>
<b>Selected-code actions</b> — select code, right-click, then pick
<i>Explain / Refactor / Write Tests / Fix Bug / Add Docs with CodeSetu</i>.
</li>
<li>
<b>Switch on the fly</b> — click the <b>provider · model</b> chip in the composer to
change models or providers without leaving the chat.
</li>
<li>
<b>Workspace skills</b> — drop <code>.codesetu/skills/*.md</code> files at the project
root; they're folded into the system prompt for every message.
</li>
</ul>

<h3>Try these prompts</h3>
<ul class="welcome-bullets">
<li>
<b>Understand:</b> &ldquo;Explain what this file does and how the main function fits
in.&rdquo;
</li>
<li>
<b>Refactor:</b> &ldquo;Refactor the selected function for readability without
changing behavior. List the changes.&rdquo;
</li>
<li>
<b>Test:</b> &ldquo;Write unit tests for the selected function — happy path plus edge
cases.&rdquo;
</li>
<li>
<b>Debug:</b> &ldquo;Why might this function throw a null-pointer exception? Give the
smallest safe fix.&rdquo;
</li>
<li>
<b>Document:</b> &ldquo;Add JSDoc comments to the selected function — describe each
parameter and the return value.&rdquo;
</li>
<li>
<b>Translate:</b> &ldquo;Translate the selected Python snippet to TypeScript with
equivalent types.&rdquo;
</li>
<li>
<b>Generate:</b> &ldquo;Generate a regex that matches Indian phone numbers (+91, 10
digits, optional spaces).&rdquo;
</li>
</ul>

<p class="welcome-tip">
<b>Tip:</b> Open the <b>+</b> menu in the composer to toggle
<b>Include IDE context</b> off when asking a general question — keeps prompts small and
your code out of the request.
</p>
</div>
</section>
<form id="chat-form" class="composer-wrap">
<div class="composer-shell">
<textarea id="message" aria-label="Message" placeholder="Ask CodeSetu"></textarea>
Expand Down Expand Up @@ -471,6 +650,9 @@
const includeContext = document.getElementById("include-context");
const modelChip = document.getElementById("model-chip");
const modelLabel = document.getElementById("model-label");
const welcome = document.getElementById("welcome");
const welcomeConfigure = document.getElementById("welcome-configure");
const welcomeDocs = document.getElementById("welcome-docs");
let activeAssistantMessage;
let activeAssistantRaw = "";

Expand Down Expand Up @@ -589,6 +771,8 @@
message.textContent = text;
}
transcript.appendChild(message);
// First real message hides the welcome panel for the rest of the session.
welcome.hidden = true;
message.scrollIntoView({ block: "end", behavior: "smooth" });
return message;
}
Expand Down Expand Up @@ -623,6 +807,14 @@
post({ type: "selectModel" });
});

welcomeConfigure.addEventListener("click", () => {
post({ type: "configureProvider" });
});

welcomeDocs.addEventListener("click", () => {
post({ type: "openUrl", url: "https://github.com/getcodesetu/codesetu" });
});

document.addEventListener("click", (event) => {
const target = event.target;
if (!(target instanceof Element)) {
Expand Down Expand Up @@ -681,6 +873,10 @@
modelLabel.textContent = message.text;
}

if (message.type === "welcome") {
welcome.hidden = !message.show;
}

if (message.type === "busy") {
const isBusy = Boolean(message.value);
send.disabled = isBusy;
Expand Down
21 changes: 21 additions & 0 deletions docs/marketplace/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# JetBrains Marketplace pages

Standalone markdown sources for the **JetBrains Marketplace Custom Pages** under
the CodeSetu vendor profile. Paste each file into the corresponding page on
[plugins.jetbrains.com](https://plugins.jetbrains.com) (the Marketplace UI
accepts markdown).

These pages complement the plugin listing description (which is rendered from
`apps/jetbrains/src/main/resources/META-INF/plugin.xml`). The listing is the
landing page; these are deeper docs.

| File | Suggested page title |
| -------------------------------------------------- | -------------------- |
| [quickstart.md](quickstart.md) | Quickstart |
| [examples.md](examples.md) | Examples |
| [providers-and-models.md](providers-and-models.md) | Providers & Models |
| [privacy-and-security.md](privacy-and-security.md) | Privacy & Security |
| [faq.md](faq.md) | FAQ |

Keep these in sync with the plugin description and `INSTALL.md` when you ship a
release.
Loading
Loading