vibetunnel/web/public/components/session-create-form.js
Mario Zechner 7f63c9e168 Fix session card UI and file browser issues
- Update asciinema player CSS to use proper aspect ratios
- Fix file browser z-index and layout issues
- Remove old unused app.ts and server.ts files
- Keep working app-new components structure

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-16 03:59:39 +02:00

193 lines
No EOL
6.7 KiB
JavaScript

var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { LitElement, html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import './file-browser.js';
let SessionCreateForm = class SessionCreateForm extends LitElement {
constructor() {
super(...arguments);
this.workingDir = '~/';
this.command = '';
this.disabled = false;
this.isCreating = false;
this.showFileBrowser = false;
}
// Disable shadow DOM to use Tailwind
createRenderRoot() {
return this;
}
handleWorkingDirChange(e) {
const input = e.target;
this.workingDir = input.value;
this.dispatchEvent(new CustomEvent('working-dir-change', {
detail: this.workingDir
}));
}
handleCommandChange(e) {
const input = e.target;
this.command = input.value;
}
handleBrowse() {
this.showFileBrowser = true;
}
handleDirectorySelected(e) {
this.workingDir = e.detail;
this.showFileBrowser = false;
}
handleBrowserCancel() {
this.showFileBrowser = false;
}
async handleCreate() {
if (!this.workingDir.trim() || !this.command.trim()) {
this.dispatchEvent(new CustomEvent('error', {
detail: 'Please fill in both working directory and command'
}));
return;
}
this.isCreating = true;
const sessionData = {
command: this.parseCommand(this.command.trim()),
workingDir: this.workingDir.trim()
};
try {
const response = await fetch('/api/sessions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(sessionData)
});
if (response.ok) {
const result = await response.json();
this.command = ''; // Clear command on success
this.dispatchEvent(new CustomEvent('session-created', {
detail: result
}));
}
else {
const error = await response.json();
this.dispatchEvent(new CustomEvent('error', {
detail: `Failed to create session: ${error.error}`
}));
}
}
catch (error) {
console.error('Error creating session:', error);
this.dispatchEvent(new CustomEvent('error', {
detail: 'Failed to create session'
}));
}
finally {
this.isCreating = false;
}
}
parseCommand(commandStr) {
// Simple command parsing - split by spaces but respect quotes
const args = [];
let current = '';
let inQuotes = false;
let quoteChar = '';
for (let i = 0; i < commandStr.length; i++) {
const char = commandStr[i];
if ((char === '"' || char === "'") && !inQuotes) {
inQuotes = true;
quoteChar = char;
}
else if (char === quoteChar && inQuotes) {
inQuotes = false;
quoteChar = '';
}
else if (char === ' ' && !inQuotes) {
if (current) {
args.push(current);
current = '';
}
}
else {
current += char;
}
}
if (current) {
args.push(current);
}
return args;
}
render() {
return html `
<div class="border border-vs-accent font-mono text-sm p-4 m-4 rounded">
<div class="text-vs-assistant text-sm mb-4">Create New Session</div>
<div class="mb-4">
<div class="text-vs-muted mb-2">Working Directory:</div>
<div class="flex gap-4">
<input
type="text"
class="flex-1 bg-vs-bg text-vs-text border border-vs-border outline-none font-mono px-4 py-2"
.value=${this.workingDir}
@input=${this.handleWorkingDirChange}
placeholder="~/"
?disabled=${this.disabled || this.isCreating}
/>
<button
class="bg-vs-function text-vs-bg hover:bg-vs-highlight font-mono px-4 py-2 border-none"
@click=${this.handleBrowse}
?disabled=${this.disabled || this.isCreating}
>
browse
</button>
</div>
</div>
<div class="mb-4">
<div class="text-vs-muted mb-2">Command:</div>
<input
type="text"
class="w-full bg-vs-bg text-vs-text border border-vs-border outline-none font-mono px-4 py-2"
.value=${this.command}
@input=${this.handleCommandChange}
@keydown=${(e) => e.key === 'Enter' && this.handleCreate()}
placeholder="zsh"
?disabled=${this.disabled || this.isCreating}
/>
</div>
<button
class="bg-vs-user text-vs-text hover:bg-vs-accent font-mono px-4 py-2 border-none"
@click=${this.handleCreate}
?disabled=${this.disabled || this.isCreating || !this.workingDir.trim() || !this.command.trim()}
>
${this.isCreating ? 'creating...' : 'create'}
</button>
</div>
<file-browser
.visible=${this.showFileBrowser}
.currentPath=${this.workingDir}
@directory-selected=${this.handleDirectorySelected}
@browser-cancel=${this.handleBrowserCancel}
></file-browser>
`;
}
};
__decorate([
property({ type: String })
], SessionCreateForm.prototype, "workingDir", void 0);
__decorate([
property({ type: String })
], SessionCreateForm.prototype, "command", void 0);
__decorate([
property({ type: Boolean })
], SessionCreateForm.prototype, "disabled", void 0);
__decorate([
state()
], SessionCreateForm.prototype, "isCreating", void 0);
__decorate([
state()
], SessionCreateForm.prototype, "showFileBrowser", void 0);
SessionCreateForm = __decorate([
customElement('session-create-form')
], SessionCreateForm);
export { SessionCreateForm };
//# sourceMappingURL=session-create-form.js.map