diff --git a/app/components/AppFooter.vue b/app/components/AppFooter.vue
index 804d4a19fb..cba4d974d1 100644
--- a/app/components/AppFooter.vue
+++ b/app/components/AppFooter.vue
@@ -1,13 +1,15 @@
diff --git a/app/components/CodeMobileTreeDrawer.vue b/app/components/CodeMobileTreeDrawer.vue
index 981b1b1b95..d55e86a0ff 100644
--- a/app/components/CodeMobileTreeDrawer.vue
+++ b/app/components/CodeMobileTreeDrawer.vue
@@ -7,7 +7,7 @@ defineProps<{
baseUrl: string
}>()
-const isOpen = ref(false)
+const isOpen = shallowRef(false)
// Close drawer on navigation
const route = useRoute()
diff --git a/app/components/CodeViewer.vue b/app/components/CodeViewer.vue
index b3b95d2ee0..f77a0ca2a1 100644
--- a/app/components/CodeViewer.vue
+++ b/app/components/CodeViewer.vue
@@ -9,7 +9,7 @@ const emit = defineEmits<{
lineClick: [lineNum: number, event: MouseEvent]
}>()
-const codeRef = ref()
+const codeRef = useTemplateRef('codeRef')
// Generate line numbers array
const lineNumbers = computed(() => {
diff --git a/app/components/ConnectorModal.vue b/app/components/ConnectorModal.vue
index de81b419f7..a5ca92802e 100644
--- a/app/components/ConnectorModal.vue
+++ b/app/components/ConnectorModal.vue
@@ -4,9 +4,9 @@ const open = defineModel('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
diff --git a/app/components/ConnectorStatus.client.vue b/app/components/ConnectorStatus.client.vue
index c335bfa0f1..baf3bdecde 100644
--- a/app/components/ConnectorStatus.client.vue
+++ b/app/components/ConnectorStatus.client.vue
@@ -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…'
diff --git a/app/components/OperationsQueue.vue b/app/components/OperationsQueue.vue
index acefe33212..b37daca418 100644
--- a/app/components/OperationsQueue.vue
+++ b/app/components/OperationsQueue.vue
@@ -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(() =>
diff --git a/app/components/OrgMembersPanel.vue b/app/components/OrgMembersPanel.vue
index 5e3b02cff9..03f9429ebf 100644
--- a/app/components/OrgMembersPanel.vue
+++ b/app/components/OrgMembersPanel.vue
@@ -21,27 +21,27 @@ const {
} = useConnector()
// Members data: { username: role }
-const members = ref>({})
-const isLoading = ref(false)
-const error = ref(null)
+const members = shallowRef>({})
+const isLoading = shallowRef(false)
+const error = shallowRef(null)
// Team membership data: { teamName: [members] }
const teamMembers = ref>({})
-const isLoadingTeams = ref(false)
+const isLoadingTeams = shallowRef(false)
// Search/filter
-const searchQuery = ref('')
-const filterRole = ref<'all' | 'developer' | 'admin' | 'owner'>('all')
-const filterTeam = ref(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(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('') // 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('') // Empty string means "developers" (default)
+const isAddingMember = shallowRef(false)
// Role priority for sorting
const rolePriority = { owner: 0, admin: 1, developer: 2 }
diff --git a/app/components/OrgTeamsPanel.vue b/app/components/OrgTeamsPanel.vue
index 22606bdc6c..1cbe5d4773 100644
--- a/app/components/OrgTeamsPanel.vue
+++ b/app/components/OrgTeamsPanel.vue
@@ -17,32 +17,32 @@ const {
} = useConnector()
// Teams data
-const teams = ref([])
+const teams = shallowRef([])
const teamUsers = ref>({})
-const isLoadingTeams = ref(false)
+const isLoadingTeams = shallowRef(false)
const isLoadingUsers = ref>({})
-const error = ref(null)
+const error = shallowRef(null)
// Org members (to check if user needs to be added to org first)
-const orgMembers = ref>({})
+const orgMembers = shallowRef>({})
// 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>(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(null)
-const newUserUsername = ref('')
-const isAddingUser = ref(false)
+const showAddUserFor = shallowRef(null)
+const newUserUsername = shallowRef('')
+const isAddingUser = shallowRef(false)
// Filtered and sorted teams
const filteredTeams = computed(() => {
diff --git a/app/components/PackageAccessControls.vue b/app/components/PackageAccessControls.vue
index 72dd764ab1..90a33d21b9 100644
--- a/app/components/PackageAccessControls.vue
+++ b/app/components/PackageAccessControls.vue
@@ -23,17 +23,17 @@ const orgName = computed(() => {
})
// Data
-const collaborators = ref>({})
-const teams = ref([])
-const isLoadingCollaborators = ref(false)
-const isLoadingTeams = ref(false)
-const error = ref(null)
+const collaborators = shallowRef>({})
+const teams = shallowRef([])
+const isLoadingCollaborators = shallowRef(false)
+const isLoadingTeams = shallowRef(false)
+const error = shallowRef(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(() => {
@@ -136,10 +136,10 @@ 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()
@@ -147,25 +147,6 @@ watch(
},
{ 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()
- }
-})
diff --git a/app/components/PackageCard.vue b/app/components/PackageCard.vue
index 91d6e0369b..38b10891ba 100644
--- a/app/components/PackageCard.vue
+++ b/app/components/PackageCard.vue
@@ -1,6 +1,4 @@
diff --git a/app/components/PackageList.vue b/app/components/PackageList.vue
index ea44d408ea..61840109f0 100644
--- a/app/components/PackageList.vue
+++ b/app/components/PackageList.vue
@@ -32,7 +32,7 @@ const emit = defineEmits<{
}>()
// Reference to WindowVirtualizer for infinite scroll detection
-const listRef = ref()
+const listRef = useTemplateRef('listRef')
// Set up infinite scroll if hasMore is provided
const hasMore = computed(() => props.hasMore ?? false)
@@ -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],
diff --git a/app/components/PackageMaintainers.vue b/app/components/PackageMaintainers.vue
index 52e5bdb093..5566954ff2 100644
--- a/app/components/PackageMaintainers.vue
+++ b/app/components/PackageMaintainers.vue
@@ -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)
@@ -30,9 +30,9 @@ const orgName = computed(() => {
})
// Access data: who has access and via what
-const collaborators = ref>({})
+const collaborators = shallowRef>({})
const teamMembers = ref>({}) // team -> members
-const isLoadingAccess = ref(false)
+const isLoadingAccess = shallowRef(false)
// Compute access source for each maintainer
const maintainerAccess = computed(() => {
@@ -142,7 +142,7 @@ 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()
@@ -150,13 +150,6 @@ watch(
},
{ immediate: true },
)
-
-// Refresh data when operations complete
-watch(lastExecutionTime, () => {
- if (isConnected.value && orgName.value) {
- loadAccessInfo()
- }
-})
diff --git a/app/components/PackagePlaygrounds.vue b/app/components/PackagePlaygrounds.vue
index aa553183ec..b8388bfa39 100644
--- a/app/components/PackagePlaygrounds.vue
+++ b/app/components/PackagePlaygrounds.vue
@@ -1,5 +1,4 @@
diff --git a/app/components/UserCombobox.vue b/app/components/UserCombobox.vue
index e7d2b51a10..5257fcd238 100644
--- a/app/components/UserCombobox.vue
+++ b/app/components/UserCombobox.vue
@@ -15,11 +15,10 @@ const emit = defineEmits<{
select: [username: string, isInSuggestions: boolean]
}>()
-const inputValue = ref('')
-const isOpen = ref(false)
-const highlightedIndex = ref(-1)
-const inputRef = ref(null)
-const listRef = ref(null)
+const inputValue = shallowRef('')
+const isOpen = shallowRef(false)
+const highlightedIndex = shallowRef(-1)
+const listRef = useTemplateRef('listRef')
// Generate unique ID for accessibility
const inputId = useId()
@@ -134,10 +133,7 @@ watch(highlightedIndex, index => {
})
// Check for reduced motion preference
-const prefersReducedMotion = ref(false)
-onMounted(() => {
- prefersReducedMotion.value = window.matchMedia('(prefers-reduced-motion: reduce)').matches
-})
+const prefersReducedMotion = useMediaQuery('(prefers-reduced-motion: reduce)')
@@ -145,7 +141,6 @@ onMounted(() => {
| undefined>,
) {
- const outdated = ref>({})
+ const outdated = shallowRef>({})
async function fetchOutdatedInfo(deps: Record | undefined) {
if (!deps || Object.keys(deps).length === 0) {
diff --git a/app/composables/useSelectedPackageManager.ts b/app/composables/useSelectedPackageManager.ts
index f4062b201e..ab60093239 100644
--- a/app/composables/useSelectedPackageManager.ts
+++ b/app/composables/useSelectedPackageManager.ts
@@ -1,5 +1,3 @@
-import { useLocalStorage } from '@vueuse/core'
-
export function useSelectedPackageManager() {
return useLocalStorage('npmx-pm', 'npm')
}
diff --git a/app/composables/useVirtualInfiniteScroll.ts b/app/composables/useVirtualInfiniteScroll.ts
index 5b904069ec..5eba4248d1 100644
--- a/app/composables/useVirtualInfiniteScroll.ts
+++ b/app/composables/useVirtualInfiniteScroll.ts
@@ -13,7 +13,7 @@ export interface WindowVirtualizerHandle {
export interface UseVirtualInfiniteScrollOptions {
/** Reference to the WindowVirtualizer component */
- listRef: Ref
+ listRef: Ref
/** Current item count */
itemCount: Ref
/** Whether there are more items to load */
@@ -48,10 +48,10 @@ export function useVirtualInfiniteScroll(options: UseVirtualInfiniteScrollOption
} = options
// Track last fetched count to prevent duplicate fetches
- const fetchedCountRef = ref(-1)
+ const fetchedCountRef = shallowRef(-1)
// Track current page to avoid unnecessary updates
- const currentPage = ref(1)
+ const currentPage = shallowRef(1)
function handleScroll() {
const list = listRef.value