Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7faaedd
perf(footer): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
050885d
perf(tooltip): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
0f52a6c
perf(claim-package-modal): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
6c4fa64
perf(code-mobile-tree-drawer): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
08be1cb
perf(code-viewer): use `useTemplateRef` instead deep ref
OrbisK Jan 26, 2026
9d6b149
perf(connector-modal): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
b10d8d6
perf(connector-status): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
f84b9e7
perf(operations-queue): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
6c460b5
perf(org-members-panel): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
14bd054
perf(org-teams-panel): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
9bd0882
perf(package-access-controls): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
8c811fc
chore(package-card): removed imported auto import
OrbisK Jan 26, 2026
629f366
perf(package-dependencies): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
df05aba
perf(package-install-scripts): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
17640fe
perf(package-list): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
70affdc
perf(package-maintainers): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
4d3ef43
perf(package-playgrounds): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
e97fafa
perf(package-versions): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
c8aed17
perf(package-vulnerabilities): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
b07ba45
perf(scroll-to-top): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
1f23466
perf(user-combobox): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
3ebbfa7
perf(composables): use `shallowRef` instead deep ref
OrbisK Jan 26, 2026
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
18 changes: 8 additions & 10 deletions app/components/AppFooter.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<script setup lang="ts">
const isMounted = ref(false)
const isVisible = ref(false)
const isScrollable = ref(true)
const lastScrollY = ref(0)
const footerRef = ref<HTMLElement>()
const isMounted = shallowRef(false)
const isVisible = shallowRef(false)
const isScrollable = shallowRef(true)
const lastScrollY = shallowRef(0)
const footerRef = useTemplateRef('footerRef')

// Check if CSS scroll-state container queries are supported
// Once this becomes baseline, we can remove the JS scroll handling entirely
const supportsScrollStateQueries = ref(false)
const supportsScrollStateQueries = useSupported(() => {
return isMounted.value && CSS.supports('container-type', 'scroll-state')
})

function checkScrollable() {
return document.documentElement.scrollHeight > window.innerHeight
Expand Down Expand Up @@ -52,10 +54,6 @@ useEventListener('scroll', onScroll, { passive: true })
useEventListener('resize', onResize, { passive: true })

onMounted(() => {
// Feature detect CSS scroll-state container queries (Chrome 133+)
// @see https://developer.mozilla.org/en-US/docs/Web/CSS/@container#scroll-state_container_descriptors
supportsScrollStateQueries.value = CSS.supports('container-type', 'scroll-state')

nextTick(() => {
lastScrollY.value = window.scrollY
isScrollable.value = checkScrollable()
Expand Down
2 changes: 1 addition & 1 deletion app/components/AppTooltip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const props = defineProps<{
position?: 'top' | 'bottom' | 'left' | 'right'
}>()

const isVisible = ref(false)
const isVisible = shallowRef(false)
const tooltipId = useId()

const positionClasses: Record<string, string> = {
Expand Down
12 changes: 6 additions & 6 deletions app/components/ClaimPackageModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ const {
} = useConnector()

// Fetch name availability when modal opens
const checkResult = ref<CheckNameResult | null>(null)
const checkResult = shallowRef<CheckNameResult | null>(null)

const isChecking = ref(false)
const isPublishing = ref(false)
const publishError = ref<string | null>(null)
const publishSuccess = ref(false)
const isChecking = shallowRef(false)
const isPublishing = shallowRef(false)
const publishError = shallowRef<string | null>(null)
const publishSuccess = shallowRef(false)

async function checkAvailability() {
isChecking.value = true
Expand Down Expand Up @@ -125,7 +125,7 @@ const previewPackageJson = computed(() => {
}
})

const connectorModalOpen = ref(false)
const connectorModalOpen = shallowRef(false)
</script>

<template>
Expand Down
2 changes: 1 addition & 1 deletion app/components/CodeMobileTreeDrawer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defineProps<{
baseUrl: string
}>()

const isOpen = ref(false)
const isOpen = shallowRef(false)

// Close drawer on navigation
const route = useRoute()
Expand Down
2 changes: 1 addition & 1 deletion app/components/CodeViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const emit = defineEmits<{
lineClick: [lineNum: number, event: MouseEvent]
}>()

const codeRef = ref<HTMLElement>()
const codeRef = useTemplateRef('codeRef')

// Generate line numbers array
const lineNumbers = computed(() => {
Expand Down
6 changes: 3 additions & 3 deletions app/components/ConnectorModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ const open = defineModel<boolean>('open', { default: false })
const { isConnected, isConnecting, npmUser, error, hasOperations, connect, disconnect } =
useConnector()

const tokenInput = ref('')
const portInput = ref('31415')
const copied = ref(false)
const tokenInput = shallowRef('')
const portInput = shallowRef('31415')
const copied = shallowRef(false)

async function handleConnect() {
const port = Number.parseInt(portInput.value, 10) || 31415
Expand Down
4 changes: 2 additions & 2 deletions app/components/ConnectorStatus.client.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
const { isConnected, isConnecting, npmUser, error, activeOperations, hasPendingOperations } =
useConnector()

const showModal = ref(false)
const showTooltip = ref(false)
const showModal = shallowRef(false)
const showTooltip = shallowRef(false)

const statusText = computed(() => {
if (isConnecting.value) return 'connecting…'
Expand Down
4 changes: 2 additions & 2 deletions app/components/OperationsQueue.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const {
refreshState,
} = useConnector()

const isExecuting = ref(false)
const otpInput = ref('')
const isExecuting = shallowRef(false)
const otpInput = shallowRef('')

/** Check if any active operation needs OTP */
const hasOtpFailures = computed(() =>
Expand Down
28 changes: 14 additions & 14 deletions app/components/OrgMembersPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,27 @@ const {
} = useConnector()

// Members data: { username: role }
const members = ref<Record<string, 'developer' | 'admin' | 'owner'>>({})
const isLoading = ref(false)
const error = ref<string | null>(null)
const members = shallowRef<Record<string, 'developer' | 'admin' | 'owner'>>({})
const isLoading = shallowRef(false)
const error = shallowRef<string | null>(null)

// Team membership data: { teamName: [members] }
const teamMembers = ref<Record<string, string[]>>({})
const isLoadingTeams = ref(false)
const isLoadingTeams = shallowRef(false)

// Search/filter
const searchQuery = ref('')
const filterRole = ref<'all' | 'developer' | 'admin' | 'owner'>('all')
const filterTeam = ref<string | null>(null)
const sortBy = ref<'name' | 'role'>('name')
const sortOrder = ref<'asc' | 'desc'>('asc')
const searchQuery = shallowRef('')
const filterRole = shallowRef<'all' | 'developer' | 'admin' | 'owner'>('all')
const filterTeam = shallowRef<string | null>(null)
const sortBy = shallowRef<'name' | 'role'>('name')
const sortOrder = shallowRef<'asc' | 'desc'>('asc')

// Add member form
const showAddMember = ref(false)
const newUsername = ref('')
const newRole = ref<'developer' | 'admin' | 'owner'>('developer')
const newTeam = ref<string>('') // Empty string means "developers" (default)
const isAddingMember = ref(false)
const showAddMember = shallowRef(false)
const newUsername = shallowRef('')
const newRole = shallowRef<'developer' | 'admin' | 'owner'>('developer')
const newTeam = shallowRef<string>('') // Empty string means "developers" (default)
const isAddingMember = shallowRef(false)

// Role priority for sorting
const rolePriority = { owner: 0, admin: 1, developer: 2 }
Expand Down
26 changes: 13 additions & 13 deletions app/components/OrgTeamsPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,32 @@ const {
} = useConnector()

// Teams data
const teams = ref<string[]>([])
const teams = shallowRef<string[]>([])
const teamUsers = ref<Record<string, string[]>>({})
const isLoadingTeams = ref(false)
const isLoadingTeams = shallowRef(false)
const isLoadingUsers = ref<Record<string, boolean>>({})
const error = ref<string | null>(null)
const error = shallowRef<string | null>(null)

// Org members (to check if user needs to be added to org first)
const orgMembers = ref<Record<string, 'developer' | 'admin' | 'owner'>>({})
const orgMembers = shallowRef<Record<string, 'developer' | 'admin' | 'owner'>>({})

// Search/filter
const searchQuery = ref('')
const sortBy = ref<'name' | 'members'>('name')
const sortOrder = ref<'asc' | 'desc'>('asc')
const searchQuery = shallowRef('')
const sortBy = shallowRef<'name' | 'members'>('name')
const sortOrder = shallowRef<'asc' | 'desc'>('asc')

// Expanded teams (to show members)
const expandedTeams = ref<Set<string>>(new Set())

// Create team form
const showCreateTeam = ref(false)
const newTeamName = ref('')
const isCreatingTeam = ref(false)
const showCreateTeam = shallowRef(false)
const newTeamName = shallowRef('')
const isCreatingTeam = shallowRef(false)

// Add user form (per team)
const showAddUserFor = ref<string | null>(null)
const newUserUsername = ref('')
const isAddingUser = ref(false)
const showAddUserFor = shallowRef<string | null>(null)
const newUserUsername = shallowRef('')
const isAddingUser = shallowRef(false)

// Filtered and sorted teams
const filteredTeams = computed(() => {
Expand Down
43 changes: 12 additions & 31 deletions app/components/PackageAccessControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ const orgName = computed(() => {
})

// Data
const collaborators = ref<Record<string, 'read-only' | 'read-write'>>({})
const teams = ref<string[]>([])
const isLoadingCollaborators = ref(false)
const isLoadingTeams = ref(false)
const error = ref<string | null>(null)
const collaborators = shallowRef<Record<string, 'read-only' | 'read-write'>>({})
const teams = shallowRef<string[]>([])
const isLoadingCollaborators = shallowRef(false)
const isLoadingTeams = shallowRef(false)
const error = shallowRef<string | null>(null)

// Grant access form
const showGrantAccess = ref(false)
const selectedTeam = ref('')
const permission = ref<'read-only' | 'read-write'>('read-only')
const isGranting = ref(false)
const showGrantAccess = shallowRef(false)
const selectedTeam = shallowRef('')
const permission = shallowRef<'read-only' | 'read-write'>('read-only')
const isGranting = shallowRef(false)

// Computed collaborator list with type detection
const collaboratorList = computed(() => {
Expand Down Expand Up @@ -136,36 +136,17 @@ async function handleRevokeAccess(collaboratorName: string) {
await addOperation(operation)
}

// Load on mount when connected
// Reload when package changes
watch(
isConnected,
connected => {
() => [isConnected.value, props.packageName, lastExecutionTime.value],
([connected]) => {
if (connected && orgName.value) {
loadCollaborators()
loadTeams()
}
},
{ immediate: true },
)

// Reload when package changes
watch(
() => props.packageName,
() => {
if (isConnected.value && orgName.value) {
loadCollaborators()
loadTeams()
}
},
)

// Refresh data when operations complete
watch(lastExecutionTime, () => {
if (isConnected.value && orgName.value) {
loadCollaborators()
loadTeams()
}
})
</script>

<template>
Expand Down
2 changes: 0 additions & 2 deletions app/components/PackageCard.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<script setup lang="ts">
import type { NpmSearchResult } from '#shared/types'

defineProps<{
/** The search result object containing package data */
result: NpmSearchResult
Expand Down
6 changes: 3 additions & 3 deletions app/components/PackageDependencies.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ const props = defineProps<{
const outdatedDeps = useOutdatedDependencies(() => props.dependencies)

// Expanded state for each section
const depsExpanded = ref(false)
const peerDepsExpanded = ref(false)
const optionalDepsExpanded = ref(false)
const depsExpanded = shallowRef(false)
const peerDepsExpanded = shallowRef(false)
const optionalDepsExpanded = shallowRef(false)

// Sort dependencies alphabetically
const sortedDependencies = computed(() => {
Expand Down
2 changes: 1 addition & 1 deletion app/components/PackageInstallScripts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const sortedNpxDeps = computed(() => {
return Object.entries(props.installScripts.npxDependencies).sort(([a], [b]) => a.localeCompare(b))
})

const isExpanded = ref(false)
const isExpanded = shallowRef(false)
</script>

<template>
Expand Down
4 changes: 2 additions & 2 deletions app/components/PackageList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const emit = defineEmits<{
}>()

// Reference to WindowVirtualizer for infinite scroll detection
const listRef = ref<WindowVirtualizerHandle>()
const listRef = useTemplateRef<WindowVirtualizerHandle>('listRef')

// Set up infinite scroll if hasMore is provided
const hasMore = computed(() => props.hasMore ?? false)
Expand All @@ -52,7 +52,7 @@ const { handleScroll, scrollToPage } = useVirtualInfiniteScroll({
})

// Scroll to initial page once list is ready and has items
const hasScrolledToInitial = ref(false)
const hasScrolledToInitial = shallowRef(false)

watch(
[() => props.results.length, () => props.initialPage, listRef],
Expand Down
19 changes: 6 additions & 13 deletions app/components/PackageMaintainers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const {
listTeamUsers,
} = useConnector()

const showAddOwner = ref(false)
const newOwnerUsername = ref('')
const isAdding = ref(false)
const showAddOwner = shallowRef(false)
const newOwnerUsername = shallowRef('')
const isAdding = shallowRef(false)

// Show admin controls when connected (let npm CLI handle permission errors)
const canManageOwners = computed(() => isConnected.value)
Expand All @@ -30,9 +30,9 @@ const orgName = computed(() => {
})

// Access data: who has access and via what
const collaborators = ref<Record<string, 'read-only' | 'read-write'>>({})
const collaborators = shallowRef<Record<string, 'read-only' | 'read-write'>>({})
const teamMembers = ref<Record<string, string[]>>({}) // team -> members
const isLoadingAccess = ref(false)
const isLoadingAccess = shallowRef(false)

// Compute access source for each maintainer
const maintainerAccess = computed(() => {
Expand Down Expand Up @@ -142,21 +142,14 @@ async function handleRemoveOwner(username: string) {

// Load access info when connected and for scoped packages
watch(
[isConnected, () => props.packageName],
[isConnected, () => props.packageName, lastExecutionTime],
([connected]) => {
if (connected && orgName.value) {
loadAccessInfo()
}
},
{ immediate: true },
)

// Refresh data when operations complete
watch(lastExecutionTime, () => {
if (isConnected.value && orgName.value) {
loadAccessInfo()
}
})
</script>

<template>
Expand Down
9 changes: 4 additions & 5 deletions app/components/PackagePlaygrounds.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script setup lang="ts">
import { onClickOutside } from '@vueuse/core'
import type { PlaygroundLink } from '#shared/types'

const props = defineProps<{
Expand Down Expand Up @@ -41,10 +40,10 @@ function getColor(provider: string): string {
}

// Dropdown state
const isOpen = ref(false)
const dropdownRef = ref<HTMLElement>()
const menuRef = ref<HTMLElement>()
const focusedIndex = ref(-1)
const isOpen = shallowRef(false)
const dropdownRef = useTemplateRef('dropdownRef')
const menuRef = useTemplateRef('menuRef')
const focusedIndex = shallowRef(-1)

onClickOutside(dropdownRef, () => {
isOpen.value = false
Expand Down
Loading