I run a self-hosted libredesk instance and wanted to be able to triage tickets from my phone.
Rather than open a feature request, I built it and deployed it, so I am able to showcase the result directly instead of discussing about theoretical concepts.
I want to be upfront that I understand the current behaviour is deliberate, not an oversight.
The widget app uses width=device-width while the dashboard pins width=1280, initial-scale=0.29, and SmallScreenOverlay.vue exists with a scale(3) tuned specifically to cancel that out.
So the question here is not "you forgot about mobile", it is "would you consider changing your mind, and would you take a PR for it".
Related: #106 asked for this and was closed by the stale bot without a reply, so I could not tell what your position actually is.
What it looks like
Screenshots (based on v2.6.1):
Details
What works
The full triage loop on a phone, with no pinch-zooming: log in, reach an inbox via the drawer, read the conversation list, open a conversation and read the whole thread, write and send a reply, open an attachment, go back to the list.
How it is built
The interesting part is how little of this is new code.
Most of the machinery is already in the repo and simply unreachable while the viewport is pinned:
- The mobile drawer already exists.
shared-ui/components/ui/sidebar/Sidebar.vue renders a Sheet when isMobile, driven by the useMediaQuery('(max-width: 768px)') in SidebarProvider.vue, and the SidebarTriggers are already mounted in PageHeader.vue and ConversationList.vue. It never fires because the media query cannot match at a fixed 1280px layout viewport. The main blocker was collapsible="none" on the icon rail in App.vue, which short-circuits the Sheet branch.
- The inbox router is already nested.
router/index.js makes the conversation a real grandchild route with its own URL, so list-to-detail push navigation on mobile needed no router changes at all.
tailwind.config.cjs already maps h-screen to 100dvh.
DataTable.vue already uses [@media(hover:hover)] for touch, so I followed that existing pattern rather than inventing one.
The genuinely new work is the two nested ResizablePanelGroups in InboxLayout.vue and ConversationDetailView.vue.
Their minimum sizes are percentages of the window, so at 390px the thread pane computes to roughly 205px and the contact sidebar to roughly 117px.
That cannot be fixed with utility classes, so below the breakpoint the splitters are replaced by a route-driven single pane, and the contact sidebar becomes a Sheet driven by the existing CONVERSATION_SIDEBAR_TOGGLE emitter.
Everything is frontend-only.
No Go, no schema, no API changes.
If you are open to it
I would rather send this as a few reviewable PRs than one large diff.
Conveniently, most of it is desktop-neutral by construction and could land independently of the more opinionated inbox restructure:
- An
isMobile composable, plus removing some scoped CSS in Sidebar.vue/App.vue that hardcodes desktop sidebar positioning and would misposition a SheetContent.
- Responsive insets and scrolling on
DialogContent/AlertDialogContent, which currently go edge-to-edge with no max-h on a narrow viewport.
- Touch-visible variants of the actions that are currently
opacity-0 group-hover:opacity-100 and therefore unreachable on touch: deleting a private note, downloading an attachment, editing a custom attribute, and the conversation-list selection checkbox that gates all bulk actions.
- The inbox layout restructure.
- Flipping the viewport meta and removing
SmallScreenOverlay.vue.
Steps 1 to 3 are arguably worth having regardless of whether you want mobile, particularly step 3.
Happy to adjust the approach, split it differently, or drop it entirely if this is not a direction you want to go.
Thanks for libredesk either way!
I run a self-hosted libredesk instance and wanted to be able to triage tickets from my phone.
Rather than open a feature request, I built it and deployed it, so I am able to showcase the result directly instead of discussing about theoretical concepts.
I want to be upfront that I understand the current behaviour is deliberate, not an oversight.
The widget app uses
width=device-widthwhile the dashboard pinswidth=1280, initial-scale=0.29, andSmallScreenOverlay.vueexists with ascale(3)tuned specifically to cancel that out.So the question here is not "you forgot about mobile", it is "would you consider changing your mind, and would you take a PR for it".
Related: #106 asked for this and was closed by the stale bot without a reply, so I could not tell what your position actually is.
What it looks like
Screenshots (based on
v2.6.1):Details
What works
The full triage loop on a phone, with no pinch-zooming: log in, reach an inbox via the drawer, read the conversation list, open a conversation and read the whole thread, write and send a reply, open an attachment, go back to the list.
How it is built
The interesting part is how little of this is new code.
Most of the machinery is already in the repo and simply unreachable while the viewport is pinned:
shared-ui/components/ui/sidebar/Sidebar.vuerenders aSheetwhenisMobile, driven by theuseMediaQuery('(max-width: 768px)')inSidebarProvider.vue, and theSidebarTriggers are already mounted inPageHeader.vueandConversationList.vue. It never fires because the media query cannot match at a fixed 1280px layout viewport. The main blocker wascollapsible="none"on the icon rail inApp.vue, which short-circuits the Sheet branch.router/index.jsmakes the conversation a real grandchild route with its own URL, so list-to-detail push navigation on mobile needed no router changes at all.tailwind.config.cjsalready mapsh-screento100dvh.DataTable.vuealready uses[@media(hover:hover)]for touch, so I followed that existing pattern rather than inventing one.The genuinely new work is the two nested
ResizablePanelGroups inInboxLayout.vueandConversationDetailView.vue.Their minimum sizes are percentages of the window, so at 390px the thread pane computes to roughly 205px and the contact sidebar to roughly 117px.
That cannot be fixed with utility classes, so below the breakpoint the splitters are replaced by a route-driven single pane, and the contact sidebar becomes a
Sheetdriven by the existingCONVERSATION_SIDEBAR_TOGGLEemitter.Everything is frontend-only.
No Go, no schema, no API changes.
If you are open to it
I would rather send this as a few reviewable PRs than one large diff.
Conveniently, most of it is desktop-neutral by construction and could land independently of the more opinionated inbox restructure:
isMobilecomposable, plus removing some scoped CSS inSidebar.vue/App.vuethat hardcodes desktop sidebar positioning and would misposition aSheetContent.DialogContent/AlertDialogContent, which currently go edge-to-edge with nomax-hon a narrow viewport.opacity-0 group-hover:opacity-100and therefore unreachable on touch: deleting a private note, downloading an attachment, editing a custom attribute, and the conversation-list selection checkbox that gates all bulk actions.SmallScreenOverlay.vue.Steps 1 to 3 are arguably worth having regardless of whether you want mobile, particularly step 3.
Happy to adjust the approach, split it differently, or drop it entirely if this is not a direction you want to go.
Thanks for libredesk either way!