Skip to content

Commit 69b18ce

Browse files
authored
Feature/webview design system (#10)
* chore: refactor to remove inline css and js from webviews * chore: enhance documentation and add new guides * feat: refactor webview structure and enhance styling - Updated esbuild configuration to build both extension and webview scripts. - Introduced new CSS files for base styles, component styles, and design tokens to improve the styling of webviews. - Refactored webview scripts to utilize a common utility module for shared functionality. - Removed inline CSS and JS from webviews, enhancing maintainability and performance. - Updated TypeScript configuration to exclude webview client files from compilation. - Improved asset preview and upload widget functionality with better script management. * chore: trigger CI build * refactor: standardize return statements in webview scripts - Updated return statements in common.ts, upload-widget.ts, and input.ts to use braces for consistency and improved readability. - Ensured all conditional returns are formatted uniformly across the codebase.
1 parent 9935884 commit 69b18ce

43 files changed

Lines changed: 10467 additions & 2829 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,15 @@ Once a valid configuration has been added, the active environment will be shown
128128

129129
## Contribute
130130

131-
Got feedback or feature ideas? Open an issue.
131+
Got feedback or feature ideas? See [CONTRIBUTING.md](./CONTRIBUTING.md) or open an issue.
132132

133133
---
134134

135135
## Resources
136136

137137
- [Cloudinary Documentation](https://cloudinary.com/documentation)
138+
- [Cloudinary Upload Presets](https://cloudinary.com/documentation/upload_presets)
139+
- [Cloudinary Admin API](https://cloudinary.com/documentation/admin_api)
138140

139141
---
140142

docs/adding-features.md

Lines changed: 363 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
# Adding Features
2+
3+
This guide explains how to add new functionality to the Cloudinary VS Code Extension.
4+
5+
## Adding a New Command
6+
7+
### 1. Create the Command File
8+
9+
Create a new file in `src/commands/`:
10+
11+
```typescript
12+
// src/commands/myNewCommand.ts
13+
import * as vscode from "vscode";
14+
import { CloudinaryTreeDataProvider } from "../tree/treeDataProvider";
15+
16+
function registerMyNewCommand(
17+
context: vscode.ExtensionContext,
18+
provider: CloudinaryTreeDataProvider
19+
) {
20+
context.subscriptions.push(
21+
vscode.commands.registerCommand("cloudinary.myNewCommand", async () => {
22+
// Implementation here
23+
vscode.window.showInformationMessage("Command executed!");
24+
})
25+
);
26+
}
27+
28+
export default registerMyNewCommand;
29+
```
30+
31+
### 2. Register the Command
32+
33+
Add to `src/commands/registerCommands.ts`:
34+
35+
```typescript
36+
import registerMyNewCommand from "./myNewCommand";
37+
38+
function registerAllCommands(
39+
context: vscode.ExtensionContext,
40+
provider: CloudinaryTreeDataProvider
41+
) {
42+
// ... existing registrations
43+
registerMyNewCommand(context, provider);
44+
}
45+
```
46+
47+
### 3. Add to Package.json
48+
49+
```json
50+
{
51+
"contributes": {
52+
"commands": [
53+
{
54+
"command": "cloudinary.myNewCommand",
55+
"title": "My New Command",
56+
"category": "Cloudinary",
57+
"icon": "$(symbol-misc)"
58+
}
59+
]
60+
}
61+
}
62+
```
63+
64+
### 4. Add Menu Placement (Optional)
65+
66+
```json
67+
{
68+
"contributes": {
69+
"menus": {
70+
"view/title": [
71+
{
72+
"command": "cloudinary.myNewCommand",
73+
"when": "view == cloudinaryMediaLibrary",
74+
"group": "navigation"
75+
}
76+
],
77+
"view/item/context": [
78+
{
79+
"command": "cloudinary.myNewCommand",
80+
"when": "viewItem == asset",
81+
"group": "inline"
82+
}
83+
]
84+
}
85+
}
86+
}
87+
```
88+
89+
## Adding a New Tree Item Type
90+
91+
### 1. Add the Type
92+
93+
In `src/tree/cloudinaryItem.ts`:
94+
95+
```typescript
96+
export type CloudinaryItemType =
97+
| 'asset'
98+
| 'folder'
99+
| 'loadMore'
100+
| 'myNewType'; // Add your type
101+
```
102+
103+
### 2. Handle in Constructor
104+
105+
```typescript
106+
else if (type === 'myNewType') {
107+
this.contextValue = 'myNewType';
108+
this.iconPath = new vscode.ThemeIcon('symbol-misc');
109+
this.tooltip = 'My new item type';
110+
this.command = {
111+
command: 'cloudinary.handleMyNewType',
112+
title: 'Handle',
113+
arguments: [data],
114+
};
115+
}
116+
```
117+
118+
### 3. Create Items in Provider
119+
120+
In `src/tree/treeDataProvider.ts`:
121+
122+
```typescript
123+
const myItem = new CloudinaryItem(
124+
'Item Label',
125+
vscode.TreeItemCollapsibleState.None,
126+
'myNewType',
127+
{ customData: 'value' },
128+
this.cloudName!,
129+
this.dynamicFolders
130+
);
131+
items.push(myItem);
132+
```
133+
134+
### 4. Add Context Menu (Optional)
135+
136+
```json
137+
{
138+
"contributes": {
139+
"menus": {
140+
"view/item/context": [
141+
{
142+
"command": "cloudinary.myCommand",
143+
"when": "viewItem == myNewType"
144+
}
145+
]
146+
}
147+
}
148+
}
149+
```
150+
151+
## Adding a New Webview
152+
153+
### 1. Create the Command File
154+
155+
```typescript
156+
// src/commands/myWebview.ts
157+
import * as vscode from "vscode";
158+
import { createWebviewDocument, getScriptUri } from "../webview/webviewUtils";
159+
import { escapeHtml } from "../webview/utils/helpers";
160+
161+
let panel: vscode.WebviewPanel | undefined;
162+
163+
function registerMyWebview(context: vscode.ExtensionContext) {
164+
context.subscriptions.push(
165+
vscode.commands.registerCommand("cloudinary.openMyWebview", () => {
166+
if (panel) {
167+
panel.reveal();
168+
return;
169+
}
170+
171+
panel = vscode.window.createWebviewPanel(
172+
"cloudinaryMyWebview",
173+
"My Webview",
174+
vscode.ViewColumn.One,
175+
{
176+
enableScripts: true,
177+
localResourceRoots: [
178+
vscode.Uri.joinPath(context.extensionUri, "src", "webview", "media"),
179+
],
180+
}
181+
);
182+
183+
panel.webview.html = createWebviewDocument({
184+
title: "My Webview",
185+
webview: panel.webview,
186+
extensionUri: context.extensionUri,
187+
bodyContent: getContent(),
188+
inlineScript: "initCommon();",
189+
});
190+
191+
panel.webview.onDidReceiveMessage((message) => {
192+
switch (message.command) {
193+
case "doSomething":
194+
// Handle message
195+
break;
196+
}
197+
});
198+
199+
panel.onDidDispose(() => {
200+
panel = undefined;
201+
});
202+
})
203+
);
204+
}
205+
206+
function getContent(): string {
207+
return `
208+
<div class="container">
209+
<div class="card card--elevated">
210+
<div class="card__body">
211+
<h2>My Webview</h2>
212+
<p>Content here</p>
213+
<button class="btn btn--primary" id="myButton">Click Me</button>
214+
</div>
215+
</div>
216+
</div>
217+
`;
218+
}
219+
220+
export default registerMyWebview;
221+
```
222+
223+
### 2. Add Custom JavaScript (Optional)
224+
225+
Create `src/webview/media/scripts/my-webview.js`:
226+
227+
```javascript
228+
/**
229+
* My Webview functionality.
230+
*/
231+
232+
function initMyWebview() {
233+
const button = document.getElementById('myButton');
234+
if (button) {
235+
button.addEventListener('click', () => {
236+
vscode.postMessage({ command: 'doSomething' });
237+
});
238+
}
239+
}
240+
```
241+
242+
Update the webview to include it:
243+
244+
```typescript
245+
const myScriptUri = getScriptUri(panel.webview, context.extensionUri, "my-webview.js");
246+
247+
panel.webview.html = createWebviewDocument({
248+
// ...
249+
additionalScripts: [myScriptUri],
250+
inlineScript: "initCommon(); initMyWebview();",
251+
});
252+
```
253+
254+
### 3. Register and Add to Package.json
255+
256+
Same as adding a command (see above).
257+
258+
## Adding Configuration Options
259+
260+
### 1. Update Configuration Interface
261+
262+
In `src/config/configUtils.ts`:
263+
264+
```typescript
265+
export interface CloudinaryEnvironment {
266+
apiKey: string;
267+
apiSecret: string;
268+
uploadPreset?: string;
269+
myNewOption?: string; // Add your option
270+
}
271+
```
272+
273+
### 2. Use in Code
274+
275+
```typescript
276+
const myOption = provider.getConfig().myNewOption || 'default';
277+
```
278+
279+
### 3. Document the Option
280+
281+
Update `docs/configuration.md` with the new option.
282+
283+
## Common Patterns
284+
285+
### Error Handling
286+
287+
```typescript
288+
import { handleCloudinaryError } from '../utils/cloudinaryErrorHandler';
289+
290+
try {
291+
const result = await cloudinary.api.someMethod();
292+
} catch (err: any) {
293+
handleCloudinaryError('Operation failed', err);
294+
}
295+
```
296+
297+
### User Input
298+
299+
```typescript
300+
// Input box
301+
const query = await vscode.window.showInputBox({
302+
placeHolder: 'Enter search term',
303+
prompt: 'Search assets by public ID',
304+
validateInput: (value) => value ? null : 'Cannot be empty'
305+
});
306+
307+
if (!query) return; // User cancelled
308+
309+
// Quick pick
310+
const selected = await vscode.window.showQuickPick(
311+
['Option 1', 'Option 2', 'Option 3'],
312+
{ placeHolder: 'Select an option' }
313+
);
314+
```
315+
316+
### Progress Indicator
317+
318+
```typescript
319+
await vscode.window.withProgress(
320+
{
321+
location: vscode.ProgressLocation.Notification,
322+
title: 'Loading assets...',
323+
cancellable: false,
324+
},
325+
async (progress) => {
326+
progress.report({ increment: 0 });
327+
// Do work
328+
progress.report({ increment: 50 });
329+
// More work
330+
progress.report({ increment: 100 });
331+
}
332+
);
333+
```
334+
335+
### Refresh Tree View
336+
337+
```typescript
338+
// After modifying data
339+
provider.refresh(); // Fires _onDidChangeTreeData
340+
```
341+
342+
## Testing Your Changes
343+
344+
1. **Compile**: `npm run compile`
345+
2. **Launch**: Press `F5` to open Extension Development Host
346+
3. **Test**: Verify functionality works as expected
347+
4. **Reload**: Press `Ctrl+R` / `Cmd+R` after code changes
348+
349+
### Manual Testing Checklist
350+
351+
- [ ] Feature works in light and dark themes
352+
- [ ] Error cases show user-friendly messages
353+
- [ ] Keyboard navigation works
354+
- [ ] No console errors in Developer Tools
355+
356+
## Code Style Guidelines
357+
358+
1. **TypeScript strict mode** - Fix all type errors
359+
2. **Use existing patterns** - Follow conventions in similar files
360+
3. **Escape HTML** - Always use `escapeHtml()` for dynamic content
361+
4. **Use design system** - Use component classes from `components.css`
362+
5. **Handle errors** - Use `handleCloudinaryError()` for API errors
363+
6. **Document** - Add JSDoc comments for public functions

0 commit comments

Comments
 (0)