Skip to content

Commit b26a0ca

Browse files
committed
Merge branch dev into published
2 parents 3473143 + 98fba99 commit b26a0ca

File tree

8 files changed

+180
-6
lines changed

8 files changed

+180
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Changes to Calva.
44

55
## [Unreleased]
66

7+
## [2.0.520] - 2025-06-28
8+
9+
- [Add option for opening flares in side panel view](https://github.com/BetterThanTomorrow/calva/issues/2873)
10+
711
## [2.0.519] - 2025-06-09
812

913
- [Support `before` and `after` functions for refresh namespaces commands](https://github.com/BetterThanTomorrow/calva/issues/2862)

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,11 @@ Calva is an open source project with surprisingly many code contributors. We tak
248248

249249
## Telemetry
250250

251-
If the setting `telemetry.telemetryLevel` is set to `"all"`, Calva will collect some telemetry. It is basically some key events, like that Calva is started, a repl connected, something is evaluated. Currently it is 10 events.
251+
By default Calva collects some usage info. There are no cookies or anything like that involved. It is a randomly generated identifier per Calva install that is sent along some key events, like that Calva is started, a repl connected, something is evaluated. Currently it is 10 events.
252252
For an up-to-date, and full, list of events, see: https://github.com/BetterThanTomorrow/calva/blob/published/src/analytics.ts and the calls to that module.
253253

254+
To opt out of this, disable `calva.telemetryEnabled` in settings. For Calva telemetry to be logged, aLso the VS Code setting `telemetry.telemetryLevel` need to be set to `"all"`.
255+
254256
## Happy Coding
255257

256258
We hope you will find good use for Calva. Please let us know what you think. PRs welcome, file an issue or chat us up in the [#calva](https://clojurians.slack.com/messages/calva/) channel in the Clojurians Slack.

docs/site/flares.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@ To show a webpage, pass a `:url` instead of `:html` in the request:
4141
The `:key` parameter is optional and can be used to reuse the same WebView panel.
4242
If omitted, a new WebView panel will be created per request.
4343

44+
## Sidebar vs Panel Display
45+
46+
By default, flares open in a separate WebView panel alongside your code.
47+
You can also display flares in the Calva sidebar using the `:sidebar-panel?` option:
48+
49+
```clojure
50+
;; Display in a separate panel (default)
51+
(tagged-literal 'flare/html {:html "<h1>Hello, Panel!</h1>",
52+
:title "Panel Display"})
53+
54+
;; Display in the Calva sidebar
55+
(tagged-literal 'flare/html {:html "<h1>Hello, Sidebar!</h1>",
56+
:title "Sidebar Display"
57+
:sidebar-panel? true})
58+
```
59+
60+
The sidebar Flare view is located in the Calva activity bar section.
61+
4462
## Try a more interesting example
4563

4664
Let's create an SVG containing circles of varying radii and colors:
@@ -102,6 +120,7 @@ so you can do anything you can do in a browser, there's really no limit.
102120
| `:reveal` | boolean | true | If true, reveals the panel if it is not visible. |
103121
| `:column` | integer | vscode.ViewColumn.Beside | See [ViewColumn](https://code.visualstudio.com/api/references/vscode-api#ViewColumn) |
104122
| `:opts` | map | {:enableScripts true} | See [WebviewOptions](https://code.visualstudio.com/api/references/vscode-api#WebviewOptions) |
123+
| `:sidebar-panel` | boolean | false | If true, displays the content in the sidebar Flare view instead of a separate panel. |
105124

106125
### Suggestions welcome
107126

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "Calva: Clojure & ClojureScript Interactive Programming",
44
"description": "Integrated REPL, formatter, Paredit, and more. Powered by cider-nrepl and clojure-lsp.",
55
"icon": "assets/calva.png",
6-
"version": "2.0.519",
6+
"version": "2.0.520",
77
"publisher": "betterthantomorrow",
88
"author": {
99
"name": "Better Than Tomorrow",
@@ -890,6 +890,11 @@
890890
"markdownDescription": "Show the Calva Says output panel on start?",
891891
"default": false
892892
},
893+
"calva.telemetryEnabled": {
894+
"type": "boolean",
895+
"default": true,
896+
"description": "Enable or disable Calva usage telemetry. No user data is sent."
897+
},
893898
"calva.definitionProviderPriority": {
894899
"markdownDescription": "Calva can provide definitions using both the REPL connection and via clojure-lsp. Which provider should Calva prioritize when both are available?",
895900
"enum": [
@@ -3312,7 +3317,14 @@
33123317
"calva": [
33133318
{
33143319
"id": "calva.inspector",
3315-
"name": "Inspector"
3320+
"name": "Inspector",
3321+
"icon": "assets/images/calva-symbol-white.svg"
3322+
},
3323+
{
3324+
"id": "calva.flare",
3325+
"name": "Flare",
3326+
"type": "webview",
3327+
"icon": "assets/images/calva-symbol-white.svg"
33163328
}
33173329
]
33183330
},

src/analytics.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@ import * as uuid from 'uuidv4';
44
import { isUndefined } from 'lodash';
55

66
function userAllowsTelemetry(): boolean {
7+
const calvaConfig = vscode.workspace.getConfiguration('calva');
8+
const calvaTelemetryEnabled = calvaConfig.get<boolean>('telemetryEnabled', true);
79
const config = vscode.workspace.getConfiguration('telemetry');
8-
return config.get<string>('telemetryLevel', 'off') === 'all';
10+
const level = config.get<string>('telemetryLevel');
11+
if (level !== undefined) {
12+
return level === 'all' && calvaTelemetryEnabled;
13+
}
14+
return calvaTelemetryEnabled;
915
}
1016

1117
export default class Analytics {

src/extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { capitalize } from './utilities';
4848
import * as overrides from './overrides';
4949
import * as lsp from './lsp';
5050
import * as fiddleFiles from './fiddle-files';
51+
import * as flareHandler from './flare-handler';
5152
import * as output from './results-output/output';
5253
import * as inspector from './providers/inspector';
5354

@@ -100,6 +101,9 @@ async function activate(context: vscode.ExtensionContext) {
100101
inspectorDataProvider.treeView = inspectorTreeView;
101102
vscode.window.registerFileDecorationProvider(new inspector.InspectorItemDecorationProvider());
102103

104+
// Initialize flare webview provider for sidebar
105+
flareHandler.registerFlareWebviewProvider(context);
106+
103107
overrides.activate();
104108

105109
if (isDramStart) {

src/flare-handler.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type WebviewRequest = {
1111
column?: vscode.ViewColumn;
1212
opts?: any;
1313
then?: string;
14+
'sidebar-panel?'?: boolean;
1415
};
1516

1617
type ActRequest = WebviewRequest;
@@ -57,9 +58,113 @@ interface CalvaWebPanel extends vscode.WebviewPanel {
5758
url?: string;
5859
}
5960

61+
interface CalvaWebView extends vscode.WebviewView {
62+
url?: string;
63+
}
64+
6065
// keep track of open webviews that have a key
6166
// so that they can be updated in the future
6267
const calvaWebPanels: Record<string, CalvaWebPanel> = {};
68+
const calvaSidebarWebViews: Record<string, CalvaWebView> = {};
69+
70+
// WebView provider for sidebar
71+
class CalvaFlareWebviewProvider implements vscode.WebviewViewProvider {
72+
public static readonly viewType = 'calva.flare';
73+
private _view?: vscode.WebviewView;
74+
private _context: vscode.ExtensionContext;
75+
private _lastContent?: {
76+
html: string;
77+
title?: string;
78+
key?: string;
79+
};
80+
81+
constructor(private readonly context: vscode.ExtensionContext) {
82+
this._context = context;
83+
}
84+
85+
public resolveWebviewView(
86+
webviewView: vscode.WebviewView,
87+
context: vscode.WebviewViewResolveContext,
88+
_token: vscode.CancellationToken
89+
) {
90+
this._view = webviewView;
91+
92+
webviewView.webview.options = {
93+
enableScripts: true,
94+
localResourceRoots: [this._context.extensionUri],
95+
};
96+
97+
if (this._lastContent) {
98+
webviewView.webview.html = this._lastContent.html;
99+
if (this._lastContent.title) {
100+
webviewView.title = this._lastContent.title;
101+
}
102+
if (this._lastContent.key) {
103+
(webviewView as CalvaWebView).url = this._lastContent.key;
104+
calvaSidebarWebViews[this._lastContent.key] = webviewView as CalvaWebView;
105+
}
106+
} else {
107+
webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
108+
}
109+
}
110+
111+
public updateContent(html: string, title?: string, key?: string) {
112+
this._lastContent = { html, title, key };
113+
114+
if (this._view) {
115+
this._view.webview.html = html;
116+
if (title) {
117+
this._view.title = title;
118+
}
119+
if (key) {
120+
(this._view as CalvaWebView).url = key; // Store key in url field for tracking
121+
calvaSidebarWebViews[key] = this._view as CalvaWebView;
122+
}
123+
}
124+
}
125+
126+
public updateUrl(url: string, title?: string, key?: string) {
127+
const iframeHtml = urlInIframe(url);
128+
this._lastContent = { html: iframeHtml, title, key };
129+
130+
if (this._view) {
131+
this._view.webview.html = iframeHtml;
132+
if (title) {
133+
this._view.title = title;
134+
}
135+
if (key) {
136+
(this._view as CalvaWebView).url = url;
137+
calvaSidebarWebViews[key] = this._view as CalvaWebView;
138+
}
139+
}
140+
}
141+
142+
private _getHtmlForWebview(webview: vscode.Webview) {
143+
return `<!DOCTYPE html>
144+
<html lang="en">
145+
<head>
146+
<meta charset="UTF-8">
147+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
148+
<title>Calva Flare</title>
149+
</head>
150+
<body>
151+
<p>Flare content will appear here</p>
152+
</body>
153+
</html>`;
154+
}
155+
}
156+
157+
let flareWebviewProvider: CalvaFlareWebviewProvider | null = null;
158+
159+
export function registerFlareWebviewProvider(context: vscode.ExtensionContext) {
160+
flareWebviewProvider = new CalvaFlareWebviewProvider(context);
161+
context.subscriptions.push(
162+
vscode.window.registerWebviewViewProvider(
163+
CalvaFlareWebviewProvider.viewType,
164+
flareWebviewProvider
165+
)
166+
);
167+
}
63168

64169
function showWebView({
65170
title = 'WebView',
@@ -70,6 +175,7 @@ function showWebView({
70175
reveal = true,
71176
column = vscode.ViewColumn.Beside,
72177
opts = defaultWebviewOptions,
178+
'sidebar-panel?': sidebarPanel = false,
73179
}: {
74180
title?: string;
75181
key?: string;
@@ -79,7 +185,28 @@ function showWebView({
79185
reveal?: boolean;
80186
column?: vscode.ViewColumn;
81187
opts?: typeof defaultWebviewOptions;
188+
'sidebar-panel?'?: boolean;
82189
}): void {
190+
if (sidebarPanel) {
191+
if (flareWebviewProvider) {
192+
if (html) {
193+
flareWebviewProvider.updateContent(html, title, key);
194+
} else if (url) {
195+
flareWebviewProvider.updateUrl(url, title, key);
196+
}
197+
198+
if (reveal) {
199+
// Focus the Calva view container to show the sidebar
200+
void vscode.commands.executeCommand('calva.flare.focus');
201+
}
202+
} else {
203+
void vscode.window.showErrorMessage(
204+
'Sidebar flare webview not available. Please restart VS Code.'
205+
);
206+
}
207+
return;
208+
}
209+
83210
let panel: CalvaWebPanel;
84211
if (key) {
85212
panel = calvaWebPanels[key];

0 commit comments

Comments
 (0)