Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
f603824
feat(http): scaffold space registration, route and locales
antonreshetov Apr 30, 2026
463d50a
feat(http): add storage types, paths and contracts
antonreshetov Apr 30, 2026
ef372d9
feat(http): add state, frontmatter parser and disk sync
antonreshetov Apr 30, 2026
508e091
feat(http): add folders and requests storages
antonreshetov Apr 30, 2026
989970e
feat(http): wire environments, history, provider and watcher
antonreshetov Apr 30, 2026
4140784
feat(http): add API DTOs and routes for folders, requests, environmen…
antonreshetov Apr 30, 2026
ccc817c
feat(http): add execute IPC handler with undici, env interpolation an…
antonreshetov Apr 30, 2026
3927edd
refactor(http): extract shared types to src/main/types/http.ts
antonreshetov Apr 30, 2026
d91d9b1
feat(http): add renderer composables for folders, requests, environme…
antonreshetov Apr 30, 2026
624ea4c
feat(http): scaffold 3-column space layout with sidebar/editor/response
antonreshetov Apr 30, 2026
25ed720
feat(http): render sidebar tree with folders, requests and context menus
antonreshetov Apr 30, 2026
a8b1f9b
feat(http): split layout into folders sidebar, requests list and edit…
antonreshetov Apr 30, 2026
028d0aa
fix(http): use registered Http* component names and add search input
antonreshetov Apr 30, 2026
3377808
fix(http): match notes list header layout (single search row with + b…
antonreshetov Apr 30, 2026
e697cf9
feat(http): add description and enabled flags on header/query KV entries
antonreshetov May 1, 2026
6304ca5
feat(http): redesign KeyValueTable with checkbox, description column …
antonreshetov May 1, 2026
4dab428
refactor(http-editor): polish header, drop save button and rename des…
antonreshetov May 1, 2026
6f0eff4
style(http): show full uppercase method label in HttpMethodBadge
antonreshetov May 1, 2026
34a817c
refactor(http): migrate folders sidebar to UiTree with multi-select a…
antonreshetov May 1, 2026
dddd45f
feat(http): multi-select, multi-delete and drag-drop in request list
antonreshetov May 1, 2026
513842d
feat(store): add http selection state to app store
antonreshetov May 1, 2026
da0a16d
feat(http): persist selected request and folder across reloads
antonreshetov May 1, 2026
9a05182
refactor(http): remove unused send button label and folder info display
antonreshetov May 1, 2026
a7df7d5
feat(http): bidirectional sync between request URL and query params
antonreshetov May 1, 2026
786cbf7
feat(http): implement body, auth and description tabs
antonreshetov May 1, 2026
a46bc35
feat(http): polish ResponsePanel with status, headers and copy
antonreshetov May 1, 2026
cd053a8
fix(http): use Http* prefix for tab components
antonreshetov May 1, 2026
3a0da80
feat(http): replace body textarea with CM6 editor and JSON validation
antonreshetov May 1, 2026
c630f6f
feat(http): validate JSON body on blur instead of every change
antonreshetov May 1, 2026
018f9d7
fix(http): hide internal JSON parse error details from user
antonreshetov May 1, 2026
9538175
fix(http): nest response i18n keys inside editor namespace
antonreshetov May 1, 2026
0ecd8bd
fix(http): apply custom scrollbar class to overflow containers
antonreshetov May 1, 2026
7e2d0e0
feat(http): hide pre-request tab until implementation
antonreshetov May 1, 2026
fda004c
fix(http): avoid duplicate query params
antonreshetov May 1, 2026
374ed73
fix(http): refresh state after storage changes
antonreshetov May 1, 2026
f8761f4
fix(http): persist active space
antonreshetov May 1, 2026
79232a4
fix(http): localize empty request url
antonreshetov May 1, 2026
e370fc6
feat(http): support sidebar/list visibility toggle via View menu
antonreshetov May 1, 2026
d3dd144
style(http): align RequestEditor header with code/notes editor pattern
antonreshetov May 2, 2026
de73d15
style(http): drop font-mono from URL input
antonreshetov May 2, 2026
cf1ae42
feat(http): api-based search with state restore
antonreshetov May 2, 2026
6dba1f4
feat(http): environments switcher and manager dialog
antonreshetov May 2, 2026
59d984c
feat(http): variable interpolation highlight and autocomplete
antonreshetov May 2, 2026
f70559c
style(http): render {{var}} as chips
antonreshetov May 2, 2026
dd190fa
style(http): theme-aware {{var}} chip colors via tokens
antonreshetov May 2, 2026
00c9581
feat(http): add request name input above url row
antonreshetov May 2, 2026
8970cc2
style(http): use default variant for method select and url input
antonreshetov May 2, 2026
3fc379c
style(http): tweak environment and send icons in editor header
antonreshetov May 2, 2026
ff31b57
feat(http): validate request name on rename
antonreshetov May 2, 2026
20c5b37
feat(http): auto-rename request on folder change conflict
antonreshetov May 2, 2026
ce43dc1
chore: prevent auto-focus on close for environment popover
antonreshetov May 3, 2026
6ea0ea4
fix(http): make response panel resizable
antonreshetov May 3, 2026
bb70e98
feat(http): add request preview panel
antonreshetov May 3, 2026
cacac56
refactor(http): use compact preview copy action
antonreshetov May 3, 2026
743dade
refactor(http): add response header copy action
antonreshetov May 3, 2026
6660225
refactor(http): move response summary to bottom panel
antonreshetov May 3, 2026
059791a
refactor(http): reuse code viewer for response body
antonreshetov May 3, 2026
6a16809
refactor(cm): share editor extensions
antonreshetov May 3, 2026
04becfb
feat(http): highlight request preview
antonreshetov May 3, 2026
c418d76
refactor(cm): remove notes extension aliases
antonreshetov May 3, 2026
15665a8
feat(http): move environments to sidebar
antonreshetov May 3, 2026
58544f2
feat(http): track donation activity
antonreshetov May 3, 2026
f8b85ae
fix(http): align environment rows
antonreshetov May 3, 2026
57cf97d
refactor(http): align body editor with cm6 viewer
antonreshetov May 3, 2026
98d6e36
fix(http): use ui font in url input
antonreshetov May 3, 2026
ec2c1c9
feat(http): reorder math space definition in getSpaceDefinitions
antonreshetov May 3, 2026
a517457
feat(http): add send request shortcut
antonreshetov May 3, 2026
e52b35c
feat(notes): link http requests
antonreshetov May 3, 2026
960f0b9
fix(http): refresh vault changes
antonreshetov May 3, 2026
2e7935d
fix(http): interpolate preview variables
antonreshetov May 3, 2026
c8e67ad
feat(http): expand request context menu
antonreshetov May 3, 2026
19306e0
feat(http): add http preferences
antonreshetov May 3, 2026
f5740c5
fix(http): tune environment panel
antonreshetov May 3, 2026
226e7df
fix(http): confirm environment deletes
antonreshetov May 3, 2026
7da32cf
refactor(http): reuse key value table
antonreshetov May 3, 2026
619f5f0
fix(http): improve selected request item
antonreshetov May 3, 2026
934c30e
feat(http): add folder custom icons
antonreshetov May 3, 2026
c826ab2
fix(http): select first request on folder change
antonreshetov May 3, 2026
20f12a3
docs: add http space documentation
antonreshetov May 3, 2026
c757d0f
chore(docs): remove AppVersion tag from environments and requests doc…
antonreshetov May 3, 2026
c54f644
fix(http): prevent environment dialog autofocus tooltips
antonreshetov May 3, 2026
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat(http): render sidebar tree with folders, requests and context menus
  • Loading branch information
antonreshetov committed Apr 30, 2026
commit 25ed720fb3fa67f93139d2bc79b42ff5eb898456
6 changes: 5 additions & 1 deletion src/main/i18n/locales/en_US/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@
"label": "HTTP",
"tooltip": "HTTP client",
"title": "HTTP Client",
"untitledRequest": "Untitled request"
"untitledRequest": "Untitled request",
"empty": "No requests yet",
"action": {
"newRequest": "New request"
}
},
"tools": {
"label": "Tools",
Expand Down
6 changes: 5 additions & 1 deletion src/main/i18n/locales/ru_RU/ui.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@
"label": "HTTP",
"tooltip": "HTTP-клиент",
"title": "HTTP-клиент",
"untitledRequest": "Безымянный запрос"
"untitledRequest": "Безымянный запрос",
"empty": "Пока нет запросов",
"action": {
"newRequest": "Новый запрос"
}
},
"tools": {
"label": "Инструменты",
Expand Down
47 changes: 47 additions & 0 deletions src/renderer/components/http/HttpMethodBadge.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<script setup lang="ts">
import type { HttpMethod } from '~/main/types/http'
import { cn } from '@/utils'
import { cva } from 'class-variance-authority'

interface Props {
method: HttpMethod
size?: 'xs' | 'sm'
class?: string
}

const props = withDefaults(defineProps<Props>(), {
size: 'xs',
})

const variants = cva('font-mono font-semibold tracking-tight', {
variants: {
method: {
GET: 'text-emerald-600 dark:text-emerald-400',
POST: 'text-amber-600 dark:text-amber-400',
PUT: 'text-blue-600 dark:text-blue-400',
PATCH: 'text-violet-600 dark:text-violet-400',
DELETE: 'text-rose-600 dark:text-rose-400',
HEAD: 'text-cyan-600 dark:text-cyan-400',
OPTIONS: 'text-zinc-600 dark:text-zinc-400',
},
size: {
xs: 'text-[10px]',
sm: 'text-xs',
},
},
})

function shortLabel(method: HttpMethod): string {
return method === 'OPTIONS' ? 'OPT' : method === 'DELETE' ? 'DEL' : method
}
</script>

<template>
<span
:class="
cn(variants({ method: props.method, size: props.size }), props.class)
"
>
{{ shortLabel(props.method) }}
</span>
</template>
72 changes: 61 additions & 11 deletions src/renderer/components/http/HttpSidebar.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,70 @@
<script setup lang="ts">
import { useHttpFolders, useHttpRequests } from '@/composables'
import { i18n } from '@/electron'
import { FolderPlus, Plus } from 'lucide-vue-next'

const { createHttpFolderAndSelect, folderTree } = useHttpFolders()
const { createHttpRequestAndSelect, requests } = useHttpRequests()

const rootRequests = computed(() =>
requests.value.filter(r => r.folderId === null),
)

const isEmpty = computed(
() => folderTree.value.length === 0 && rootRequests.value.length === 0,
)

async function onCreateRootFolder() {
await createHttpFolderAndSelect()
}

async function onCreateRootRequest() {
await createHttpRequestAndSelect({ folderId: null })
}
</script>

<template>
<div class="bg-sidebar h-full overflow-hidden">
<div class="flex h-full flex-col">
<div class="border-border flex h-9 shrink-0 items-center border-b px-3">
<UiText class="text-muted-foreground text-xs">
{{ i18n.t("spaces.http.title") }}
</UiText>
</div>
<div class="flex-1 overflow-auto p-2">
<UiText class="text-muted-foreground text-xs">
TODO: collections + requests
</UiText>
<div class="bg-sidebar flex h-full flex-col overflow-hidden">
<div
class="border-border flex h-9 shrink-0 items-center justify-between border-b px-3"
>
<UiText class="text-muted-foreground text-xs uppercase">
{{ i18n.t("spaces.http.title") }}
</UiText>
<div class="flex items-center gap-1">
<UiActionButton
:tooltip="i18n.t('spaces.http.action.newRequest')"
@click="onCreateRootRequest"
>
<Plus class="h-4 w-4" />
</UiActionButton>
<UiActionButton
:tooltip="i18n.t('action.new.folder')"
@click="onCreateRootFolder"
>
<FolderPlus class="h-4 w-4" />
</UiActionButton>
</div>
</div>
<div class="min-h-0 flex-1 overflow-y-auto py-1">
<UiEmptyPlaceholder
v-if="isEmpty"
:text="i18n.t('spaces.http.empty')"
/>
<template v-else>
<HttpSidebarFolderItem
v-for="folder in folderTree"
:key="folder.id"
:folder="folder"
:depth="0"
/>
<HttpSidebarRequestItem
v-for="request in rootRequests"
:key="request.id"
:request="request"
:depth="0"
/>
</template>
</div>
</div>
</template>
146 changes: 146 additions & 0 deletions src/renderer/components/http/HttpSidebarFolderItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<script setup lang="ts">
import type { HttpFolderTreeItem } from '@/composables/spaces/http/useHttpFolders'
import type { HttpRequestListItem } from '@/composables/spaces/http/useHttpRequests'
import * as ContextMenu from '@/components/ui/shadcn/context-menu'
import { useHttpFolders, useHttpRequests } from '@/composables'
import { i18n } from '@/electron'
import { cn } from '@/utils'
import { ChevronRight, Folder } from 'lucide-vue-next'

interface Props {
folder: HttpFolderTreeItem
depth: number
}

const props = defineProps<Props>()

const {
createHttpFolderAndSelect,
deleteHttpFolder,
renameFolderId,
updateHttpFolder,
} = useHttpFolders()
const { createHttpRequestAndSelect, requests } = useHttpRequests()

const isOpen = computed(() => Boolean(props.folder.isOpen))
const isRenaming = computed(() => renameFolderId.value === props.folder.id)

const renameValue = ref(props.folder.name)

watch(isRenaming, (renaming) => {
if (renaming) {
renameValue.value = props.folder.name
}
})

const childRequests = computed<HttpRequestListItem[]>(() =>
requests.value.filter(r => r.folderId === props.folder.id),
)

async function toggleOpen() {
await updateHttpFolder(props.folder.id, { isOpen: isOpen.value ? 0 : 1 })
}

async function commitRename() {
const trimmed = renameValue.value.trim()
renameFolderId.value = null
if (trimmed && trimmed !== props.folder.name) {
await updateHttpFolder(props.folder.id, { name: trimmed })
}
}

function cancelRename() {
renameFolderId.value = null
}

function startRename() {
renameFolderId.value = props.folder.id
}

async function onCreateChildFolder() {
await createHttpFolderAndSelect(props.folder.id)
}

async function onCreateChildRequest() {
await createHttpRequestAndSelect({ folderId: props.folder.id })
if (!isOpen.value) {
await updateHttpFolder(props.folder.id, { isOpen: 1 })
}
}

async function onDelete() {
await deleteHttpFolder(props.folder.id)
}
</script>

<template>
<div>
<ContextMenu.ContextMenu>
<ContextMenu.ContextMenuTrigger as-child>
<div
:class="
cn(
'group flex h-7 cursor-pointer items-center gap-1 rounded px-2 text-sm',
'hover:bg-accent/60',
)
"
:style="{ paddingLeft: `${props.depth * 12 + 4}px` }"
@click="toggleOpen"
@dblclick="startRename"
>
<ChevronRight
class="h-3.5 w-3.5 shrink-0 transition-transform"
:class="{ 'rotate-90': isOpen }"
/>
<Folder class="h-4 w-4 shrink-0" />
<input
v-if="isRenaming"
v-model="renameValue"
class="bg-background ml-1 flex-1 rounded border px-1 text-sm outline-none"
autofocus
@click.stop
@blur="commitRename"
@keydown.enter.prevent="commitRename"
@keydown.esc.prevent="cancelRename"
>
<UiText
v-else
class="ml-1 flex-1 truncate text-sm"
>
{{ props.folder.name }}
</UiText>
</div>
</ContextMenu.ContextMenuTrigger>
<ContextMenu.ContextMenuContent>
<ContextMenu.ContextMenuItem @click="onCreateChildRequest">
{{ i18n.t("spaces.http.action.newRequest") }}
</ContextMenu.ContextMenuItem>
<ContextMenu.ContextMenuItem @click="onCreateChildFolder">
{{ i18n.t("action.new.folder") }}
</ContextMenu.ContextMenuItem>
<ContextMenu.ContextMenuSeparator />
<ContextMenu.ContextMenuItem @click="startRename">
{{ i18n.t("action.rename") }}
</ContextMenu.ContextMenuItem>
<ContextMenu.ContextMenuSeparator />
<ContextMenu.ContextMenuItem @click="onDelete">
{{ i18n.t("action.delete.common") }}
</ContextMenu.ContextMenuItem>
</ContextMenu.ContextMenuContent>
</ContextMenu.ContextMenu>
<div v-if="isOpen">
<HttpSidebarFolderItem
v-for="child in props.folder.children"
:key="child.id"
:folder="child"
:depth="props.depth + 1"
/>
<HttpSidebarRequestItem
v-for="request in childRequests"
:key="request.id"
:request="request"
:depth="props.depth + 1"
/>
</div>
</div>
</template>
Loading