106 Commits

Author SHA1 Message Date
Abhinav Raut 85a1b477f6 Merge pull request #475 from fredrikblau/feat/private-note-ticket-links
Add conversation reference links to private notes
2026-09-06 01:30:42 +05:30
Abhinav Raut e1c8719580 rename ticket references to conversation references 2026-09-03 11:15:38 +05:30
Abhinav Raut 3d45b5f74f fix ticket reference parsing, stored links and suggestion popup 2026-09-03 01:43:30 +05:30
Abhinav Raut 9c0d242549 rebuild the command palette and add keyboard shortcuts for conversation actions 2026-09-02 15:44:27 +05:30
Abhinav Raut e62967b804 isolate remote picker searches 2026-08-28 02:55:15 +05:30
Abhinav Raut e2ca7c7e5f avoid stale lookup results 2026-08-28 02:19:57 +05:30
Abhinav Raut 18514cd021 search and paginate agents, teams, tags and macros instead of loading them all at boot 2026-08-28 01:43:59 +05:30
Abhinav Raut 849ebbccc4 set the chart font on body instead of overriding root 2026-08-21 13:12:46 +05:30
Abhinav Raut 102e9fc625 apply Geist to toasts, charts and form controls 2026-08-21 12:59:19 +05:30
Abhinav Raut c865748ca5 switch to Geist and cache help center pages
Geist replaces Instrument Sans and is served from static/ instead of Google
Fonts, so it works on installs with no internet access.

Public help center pages are cached in Redis via fastcache with ETags, and any
admin write clears the group so edits show up on the next load.
2026-08-20 18:01:42 +05:30
Abhinav Raut 9106ea1fda fix composer closing on a blocked send, note keyboard focus, and sheet close target
The fullscreen composer closed itself before the send checks ran, so a
blocked send (empty To, missing tags, contact email not in recipients)
collapsed the editor on a phone even though nothing was sent. The close
now happens after all the guards.

The hover rewrite dropped focus-within from the private note delete menu,
so it could not be reached by keyboard on desktop. Put it back.

The mobile contact sheet hides its own close button, so the collapse icon
was the only way out at 28px. It is 44px below md now and stays 28px above.

Also dropped `overflow-wrap: break-all` from .message-bubble. break-all is
not a valid overflow-wrap value, so that line never did anything.

The rest is comment cleanup and switching `../..` imports to `@main` in the
files already being touched.
2026-08-17 16:06:38 +05:30
pat-s 0e8760af7d Merge branch 'main' into pr/mobile-dashboard
Resolve the index.html conflict by keeping the responsive viewport meta
from this branch and the noindex robots meta added on main.
2026-08-17 08:19:09 +00:00
Abhinav Raut f9fcba880a polish mobile layout and touch targets
Bumps taps to 44px across the sidebar, reply box, conversation header and
data tables, adds a `can-hover` Tailwind variant so hover-gated controls
work on touch, and makes the reports page stack on narrow screens.
2026-08-17 13:42:45 +05:30
Abhinav Raut 5c6f44378e fix timezone country mappings, phone error overlap and welcome workflow 2026-08-17 13:18:36 +05:30
Abhinav Raut 911fbe5b7b detect phone country from browser timezone in the pre-chat form
The country code was guessed from navigator.language, which is the UI
language and often has no region, so most visitors got US. It now reads
the browser timezone first and falls back to language, then US.
2026-08-17 13:10:45 +05:30
pat-s 3620ae0313 fix(ui): make dialogs fit a narrow viewport
`DialogContent` and `AlertDialogContent` are `w-full max-w-lg` with no side
inset, no max height and no vertical scrolling, and `sm:rounded-lg` only
applies from 640px. In a window narrower than that they render edge to
edge with square corners, and cannot be scrolled when taller than the
viewport.

Add a 1rem side inset, `max-h-[calc(100dvh-2rem)]` with `overflow-y-auto`,
and make the radius unconditional. At 640px and above `max-w-lg` still
governs the width, so nothing changes there.

Fixing the two primitives covers all 17 dialog call sites.
2026-08-16 23:20:02 +05:30
Abhinav Raut ee6bc6ec49 add csat responses to contact export and fix download revoke 2026-08-16 13:21:37 +05:30
Abhinav Raut 5735214d1a Merge remote-tracking branch 'origin/main' into worktree-gdpr
# Conflicts:
#	cmd/upgrade.go
#	frontend/apps/main/src/views/contact/ContactDetailView.vue
#	i18n/en-US.json
#	internal/media/queries.sql
#	internal/migrations/v2.6.0.go
#	internal/user/contact.go
#	schema.sql
2026-08-15 13:17:49 +05:30
Abhinav Raut 6f2f046910 Merge remote-tracking branch 'origin/main' into help-center 2026-08-08 00:01:50 +05:30
chahat-101 d180bfbedc Merge remote-tracking branch 'upstream/main' into network-status-banner 2026-08-07 20:57:38 +05:30
pat-s ee1acc688b fix: mount the contact sidebar collapsed when it is persisted closed
The sidebar panel's `default-size` was always `panelSizes[1]`, so when
`conversationSidebarOpen` is false the group normalised 100 and 30 and gave
the sidebar roughly a quarter of the width. Collapsing after mount does not
undo that: by the time `onMounted` runs the group has already laid out.

This was masked before, because the splitter always existed and the
`onMounted` fixup ran against it once. It becomes visible now that the
splitter is desktop-only: crossing the breakpoint upwards mounts a fresh,
expanded panel long after that callback has run.

Setting `default-size` to the collapsed size when the sidebar is persisted
closed is the documented way to mount a reka-ui panel collapsed, and it
fixes the initial-load case too.

Also add the blank line before the `break-word` comment in main.scss, which
was the only `//` comment in that file placed directly after a declaration.
2026-08-06 07:06:52 +00:00
pat-s ebce66a928 feat: mobile support for the agent dashboard
Lets an agent triage and reply to conversations from a phone. Admin and
reports are deliberately untouched, per the discussion in #470.

`apps/main/index.html` pinned `width=1280, initial-scale=0.29`, which makes
a phone lay the page out at 1280 CSS pixels and scale the document to 29
percent. Switching it to `width=device-width` requires the layout to cope
with a real viewport:

- Add `useIsMobile`, using `max-width: 767px` so it is the exact complement
  of Tailwind's `md:`. Align the sidebar primitive's own query to match; at
  exactly 768px it previously reported mobile while `md:` already applied.
- Fold the 3rem icon rail into the drawer. The mobile `Sheet` in
  `ui/sidebar/Sidebar.vue` was already implemented but unreachable, since
  the layout viewport could never match its media query. The rail is
  `collapsible="none"`, which returns before the Sheet branch, so its
  destinations move into the one drawer rather than nesting a second.
- Render the inbox one pane at a time below `md`. Both `ResizablePanelGroup`
  minimums are percentages of the window, giving a ~205px thread beside a
  ~117px contact sidebar at 390px, which no class change can fix. The
  routes are already nested, so list-to-detail is push navigation with a
  back affordance; only the desktop branch reads the persisted panel sizes.
- Show the contact sidebar as a `Sheet`. It has no intrinsic width, being
  sized entirely by its panel, and `collapse()`/`expand()` are splitter
  specific, so this is a parallel path off the same emitter event.
- Collapse the composer to a tap target that opens the existing fullscreen
  editor, and make that dialog full-bleed below `sm`; its `max-w-[60%]`
  gave a 234px modal at 390px. Inline it takes ~280px, which with a soft
  keyboard raised leaves the thread nothing.
- Widen message bubbles and trim the hardcoded 47px gutters on phones, and
  use `overflow-wrap: break-word` so ordinary words stop splitting
  mid-character. Reveal the private-note menu on touch via
  `[@media(hover:hover)]`.
- Remove `SmallScreenOverlay`, whose tuned `scale(3)` existed only to
  cancel out the old `initial-scale`.

Desktop is unchanged, verified by screenshot comparison at 1440px.
2026-08-06 06:41:27 +00:00
chahat-101 ee45e87d6b show connection status banner to agents 2026-07-26 00:37:52 +05:30
Abhinav Raut baff30fc28 add help center with public pages, admin management and AI article indexing
Agents can now publish a public knowledge base per help center.

Backend:
- new internal/helpcenter package for help centers, collections and articles
- admin JSON API behind a new help_center:manage permission
- public /hc/{slug} pages, JSON API, search and sitemap, all rate limited
- migration v2.7.0 for the new tables

AI:
- published articles with the AI flag on get embedded, so the agent answers
  from them. Unpublishing or clearing the flag removes the embeddings.

Media:
- media rows now carry a private flag. Help article images are public so the
  public pages can serve them without auth, and only agents with
  help_center:manage can upload them.

Frontend:
- admin help center list, tree view and article editor
- the old TextEditor is split into ArticleEditor and ConversationEditor over a
  shared useTextEditor composable, so article-only tools like callouts,
  collapsibles and YouTube embeds stay out of the reply box
2026-07-25 14:19:38 +05:30
Abhinav Raut d8b73c5877 retheme UI to the green palette and fix form and search bugs
Colors: the brand color moves from indigo to green in both themes, and the
sidebar, tooltip, card and link styles follow it. Three new tokens replace
hardcoded values: foreground-lighter for idle sidebar items, warning-600 for
warning text that needs 4.5:1 contrast on light backgrounds, and link for
anchors inside rendered email content. DESIGN_SYSTEM.md now lists the real
values from main.scss instead of the old indigo ones.

Forms: every button inside a form that is not the submit button now has an
explicit type. Without it the browser treats it as a submit button, so
clicking Cancel on a contact note posted an empty note, "New holiday" saved
the whole business-hours form, and pressing Enter in an SLA field deleted the
first alert row. The login page also highlights an empty password field on a
failed submit, which a broken condition prevented before, and the two
password fields on the set-password page get their own show and hide toggles.

Search: conversation and contact search now drop responses from an older
query, so clearing the box no longer repopulates the list with stale results.

Permissions: /api/v1/ai/summarize now needs messages:write, since it writes a
private note, and the menu item is hidden for agents without it.
2026-07-25 00:49:26 +05:30
Abhinav Raut 7c2e19b763 style sidebar section headers as labels and add design system doc 2026-07-23 03:52:05 +05:30
Abhinav Raut b8b2b6c9af standardize frontend design system, fix form validation, and skip continuity/CSAT messages in AI context
Design system:
- add semantic success and warning tokens (light + dark) and wire them into Tailwind
- move all hardcoded status colors (green/amber/red) onto tokens across main app and widget; keep file-type icons as identity colors
- give light mode real surface depth: gray chrome sidebars vs white content, deeper canvas gutter, crisper borders, wider gray spread so selected/hover states show
- unify radius (cards rounded-lg, controls rounded-md) and elevation (card shadow-sm, menu shadow-md)
- make reports overview colors uniform: neutral numbers, green met / red breached
- normalize the two page-title heading outliers to text-xl font-semibold

Form fixes:
- require content on AI snippets
- only include non-checkbox prechat fields when they have a value

AI context:
- skip continuity and CSAT messages in AI history, mining, and previous-conversation tools
2026-07-23 03:37:33 +05:30
Abhinav Raut 40f8284e98 strip quoted reply chains from AI agent message context
When the agent built conversation history and mined FAQs, it used the
raw message text, which included the full quoted reply chain from every
email. That wasted tokens and confused the model with old back-and-forth.

Add emailquote.go to strip quoted blocks. HTML messages get their quote
containers pruned (gmail, yahoo, protonmail, outlook markers, and
blockquotes); plain text gets trailing ">" lines and "On ... wrote:" /
"Original Message" markers trimmed. If stripping leaves nothing (a
quote-only reply or forward), we fall back to the full text so the
message is not dropped. Add the matching protonmail_quote selector to
the frontend hide-quoted-text styles so the two stay in sync.

Also fix knowledge base chunking: plain text with no block structure was
kept as one chunk and could overflow the model limit. It now packs into
size-bound pieces on sentence boundaries, and oversized atomic blocks are
flushed and truncated on their own so they can never sneak through.
2026-07-22 21:22:35 +05:30
Abhinav Raut b570502a20 remember when a provider needs max_completion_tokens and track token usage
Some OpenAI-compatible endpoints reject max_tokens and want
max_completion_tokens. Before, every request paid a 400 round trip to
learn this. Now we cache the swap per base URL and model, so once one
request adapts, later requests send the right param up front. Only adapt
when the error code is unsupported_parameter, not on any max_tokens error.

Parse the usage block from provider responses into a TokenUsage struct and
log prompt/completion/total tokens. Added debug logging of model content,
tool results, and RAG chunk text to make agent runs easier to trace.

Widget side: clean up chat bubble spacing by moving margins off the text
and onto trailing elements, and drop the bottom margin on the last
paragraph/list in rendered HTML so bubbles don't have extra padding.
2026-07-19 18:47:36 +05:30
Abhinav Raut 35d69fc1c3 fix review findings in reports overview and avatar upload
Guard avatar edits while a save is in flight. The AvatarUpload now takes
a disabled prop that blocks the file picker and the remove button, and
the profile view ignores crop/remove events while saving. This stops a
second crop or a remove from racing an ongoing update.

Only clear the pending file after the delete request succeeds, so a
failed remove keeps the cropped preview and the Save button usable.

Do not mark failed report refreshes as fresh. The fetch helpers now
report success, and lastUpdate only advances when every section loads.
A fully failed refresh no longer labels stale data as just updated.

Add an aria-label to the custom range days input so assistive tech can
name it.
2026-07-18 19:55:59 +05:30
Abhinav Raut 94334a02c6 polish reports overview, avatar upload, and conversation list UI
Reports overview now has a single range picker (7/30/90 days plus a
custom input) that drives every card at once, instead of a separate
date dropdown per card. Auto-refresh polls every 5 minutes, pauses
when the tab is hidden, and stops after an hour until you refresh.
Card titles are muted, numbers use tabular figures, and SLA colors use
theme tokens instead of raw green/red. The old DateFilter component is
gone.

Move avatar cropping into the shared AvatarUpload component and reuse
it on the profile page. Save is disabled until a new image is picked.

Conversation list emphasizes unread rows with bolder text and tighter
spacing. Empty state, MenuCard, and chart legend get small style
fixes. Consolidate several one-off i18n strings into shared keys.
2026-07-18 19:42:42 +05:30
Abhinav Raut 8ac465c51a support multiple auth headers on AI tools and tune agent timeouts
Tool auth used to be a single header/value pair. It is now a list of
headers, each value encrypted at rest and masked in the API. Editing a
tool keeps an untouched (masked) value's existing secret, matched by
header name. Covered with unit tests.

Agent tuning: a full agent run is now capped (90s for livechat since the
customer is watching, 3min for email), the per-provider-call timeout
drops 90s to 60s with 2 retries, and default worker_count goes 2 to 10
for burst headroom. Idle workers are free; the run cap is what protects
the pool.

Also adds help text and placeholders across the AI admin pages.
2026-07-18 18:11:10 +05:30
Abhinav Raut 889e219af4 move contact actions into a dropdown and share blob download helper
Put the block, export, and delete contact actions behind a single
"more" dropdown menu on the contact detail page instead of a row of
buttons.

Pull the blob-download logic (create link, click, revoke url) and the
blob error parsing out of the export and transcript handlers into two
shared helpers in utils/file.js so both call sites reuse them.

Also let the vite dev server read its API, websocket, and port targets
from env vars so a second instance can run side by side.
2026-07-18 12:47:07 +05:30
Abhinav Raut c030c9ee4b harden AI embedding and agent prompt handling, add tokenizer, and switch to a green theme
Embedding:
- Persist embedding_max_tokens. It was dropped when saving the provider config
  and always reset to the 8192 default, so self-hosted models with a smaller
  limit kept getting rejected.
- Split embedding requests to stay under the provider's item and token caps. A
  large snippet or URL import used to go in one request, hit the cap, fail, and
  get retried every minute forever.
- Skip blank chunks. An empty input string makes the API reject the whole batch.
- Add the tiktoken tokenizer (cl100k_base) with tests for token counting.

Agent prompt:
- Move contact fields, the conversation subject, and custom attributes out of
  the system prompt and into a delimited user-role block, so a crafted name or
  subject can't act as an instruction.

Frontend:
- Switch the primary and sidebar colors to a green theme.
- Show a "summarizing" info toast while the AI summary runs, and add an info
  toast variant.
- Make the snippet content box taller and the snippet dialog wider.
- Drop a hardcoded dark hover color on the scroll-to-bottom button.
2026-07-16 11:20:38 +05:30
Abhinav Raut 976c45df3e add AI provider connection test and self-heal reasoning model param errors
Reasoning models like gpt-5 reject max_tokens and non-default temperature.
The client now reads the structured 400, renames max_tokens to
max_completion_tokens or drops the bad tuning param, and retries. This
removes the old rule that only sent max_completion_tokens when reasoning
effort was set.

Provider form gets a "Test connection" button that makes one live call
with the form values and shows the provider's real error. Embedding test
also checks the returned vector length against the Dimensions field.
Add an Anthropic preset (their OpenAI-compatible endpoint) and stop
pre-filling temperature since blank is safe on every model.

Fix the shared Button loader so the spinner is visible on non-filled
variants, and polish the copilot panel: bot avatars on messages, dot
loader while thinking, cleaner input box, and tab slide-in animation.
2026-07-13 03:01:10 +05:30
Abhinav Raut 9c6a6c254d fix widget draft attachments, prechat phone default, and shadow tweaks
Draft attachments now keep url and disposition, so a restored draft can render its attachments correctly instead of losing them.

The prechat phone field now picks a country code from the browser locale when the field is required (falling back to US), so users are not forced to pick one manually. Non-required phone fields still start blank.

Also soften the widget iframe and launcher bubble box shadows to a lighter, tinted look.
2026-07-10 01:09:09 +05:30
Abhinav Raut 8a8d372a70 use one phone field in livechat JWT and fix phone validation 2026-07-09 02:27:08 +05:30
Abhinav Raut e56e3d3b0f use distinct icons for online, away and offline status dots 2026-07-09 01:50:20 +05:30
Abhinav Raut 8f658f274d add livechat pre-chat phone field and fix default launcher logo 2026-07-09 01:49:38 +05:30
Abhinav Raut 4dabc84a33 harden contact create, drop set-away-on-login, make status dots colorblind-safe 2026-07-08 12:14:36 +05:30
Abhinav Raut 0949761d04 fix contrast threshold and cache invalidation order on login 2026-07-05 20:03:31 +05:30
Abhinav Raut 62a1fc836e add livechat widget preview and misc improvements
Livechat:
- Add a live widget preview that renders in the inbox settings help rail.
- Warn when the primary or header colors are too close to the background.
- Require gradient/image background values and notice banner text when enabled.
- Show a default launcher logo in the widget when none is set, matching the preview.
- Drop the Beta badge from the livechat channel.

General settings:
- Add "set agents away on login". Agents log in as away and must go online manually.
- Add "show subject in conversation list" toggle.

Other:
- Add a keyboard shortcuts dialog from the user menu.
- Show a "snoozed until" badge on snoozed conversations.
- Contact search now matches emails partially instead of exact.
- Give outgoing message bubbles a secondary background.
2026-07-03 02:34:42 +05:30
Abhinav Raut 14a812d023 Merge pull request #371 from abhinavxd/update-font
update font family to Instrument Sans
2026-06-10 23:43:08 +05:30
Abhinav Raut d300ce26e6 feat: show day separators in conversation message list
closes https://github.com/abhinavxd/libredesk/issues/376
2026-06-10 21:20:07 +05:30
Abhinav Raut edbc026a24 Merge pull request #359 from abhinavxd/transcript
feat: add conversation transcript download and CSAT in sidebar
2026-06-07 15:29:20 +05:30
Abhinav Raut 1fccfceb4c feat: add customisable From display name template for outgoing emails
Adds a from_name_template field to email inboxes. When set and the sender is an agent, the template renders with agent and inbox variables and becomes the From display name; the inbox email address is unchanged. Any render failure, or a system-user sender, falls back to the plain inbox From so a misconfigured template never blocks delivery.

Variables: .Agent.FirstName, .Agent.LastName, .Agent.FullName, .Inbox.Name (the inbox name). Based on #249.

Co-authored-by: josephsellers <6892567+josephsellers@users.noreply.github.com>
2026-06-07 14:20:56 +05:30
Abhinav Raut cb081b4b38 update font family to Instrument Sans 2026-06-06 19:58:07 +05:30
Abhinav Raut eb6a195aaf feat: add conversation transcript download and CSAT in sidebar
Agents can download a full conversation as a .txt file from the
header menu. The conversation sidebar now shows the CSAT rating and
feedback once the customer responds.
2026-06-02 16:25:33 +05:30
Abhinav Raut 870f66c229 fix: sanitize invalid UTF-8 in incoming email, fix widget scroll flicker 2026-05-29 21:00:48 +05:30
Abhinav Raut 743b00ee22 fix: address PR review across stores, ws broadcast, and sticky scroll 2026-05-28 18:32:16 +05:30