Commit Graph
582 Commits
Author SHA1 Message Date
junvandGitHub 2980138ae0 Merge pull request #99 from wahyd4/dependabot/npm_and_yarn/echarts-6.1.0
build(deps): bump echarts from 5.6.0 to 6.1.0
2026-07-16 10:13:22 +10:00
junv bf3e2e80cf feat(links): auto-create non-existent tags on link create, lowercase all
Select2 submits raw text as the value for new tags typed into the tag
box (tags: true + createTag in link_form.html). The previous
ModelMultipleChoiceField silently dropped those values because they
were not PKs of existing tags, so new tags were never persisted.

Add TagInputField, a tolerant ModelMultipleChoiceField subclass:
  - existing tag PKs resolve as before
  - raw text is treated as a new (or case-insensitive-existing) tag
  - names are stripped and lowercased before get_or_create so the tag
    namespace stays consistent and case-insensitive duplicates are
    avoided. The Tag.save() hook still derives the slug.

LinkForm now uses TagInputField for its tags field, so both
LinkCreateView and LinkUpdateView pick up the new behaviour. The
Select2 createTag callback in link_form.html now lowercases the term
so users see immediately what will be stored.

Add 7 regression tests in TestLinkTagAutoCreate covering: new tag
created + lowercased; multiple new tags; existing tag reused (no
duplicate); mixed PK + new-text submission; no tags submitted; empty
or whitespace-only submission ignored; whitespace stripped.
2026-07-16 10:10:07 +10:00
github-actions[bot] e625ed13eb chore: update manifest image to 1.0.354 [skip ci] 2026-07-15 23:55:33 +00:00
junv 2625ef10b4 test(pricemon): pin telegram notification contract
Add integration tests covering the price-drop → Telegram alert flow:

- Sends Telegram MarkdownV2 message to global SiteSettings chat_id when
  detected price <= watcher.alert_price_threshold.
- No alert when global creds missing.
- No alert when price above threshold.
- No alert when threshold unset on watcher.
- Non-recurring watcher with an undismissed alert is not re-notified.
- Telegram API failure does not break the check (snapshot + alert row
  still persist, last_price updates).
2026-07-16 09:50:37 +10:00
github-actions[bot] b24d59a9ce chore: update manifest image to 1.0.353 [skip ci] 2026-07-15 23:41:22 +00:00
junv 4a5f0f63ee fix(pricemon): load i18n in _alerts partial and pass alerts to include
AlertsPartialView (htmx-polled every 30s from the dashboard) returned
500 because _alerts.html used {% trans %} without {% load i18n %}.
Added the load tag so the partial renders correctly.

The dashboard's inline {% include 'pricemon/_alerts.html' %} also
relied on an undefined 'alerts' context var (DashboardView sets
'active_alerts'), so the alerts panel rendered empty even when alerts
existed. Pass alerts=active_alerts explicitly via the include.

Add regression tests covering both the dashboard render with an alert
and the alerts partial endpoint.
2026-07-16 09:34:15 +10:00
github-actions[bot] d62a6cb714 chore: update manifest image to 1.0.352 [skip ci] 2026-07-15 05:56:32 +00:00
junv 3966740a4c Update 2026-07-15 15:51:37 +10:00
junv d26f332ef9 update video player! 2026-07-15 15:41:04 +10:00
junv 93f1a1ccae ui update 2026-07-15 15:37:19 +10:00
github-actions[bot] bbce62034a chore: update manifest image to 1.0.351 [skip ci] 2026-07-15 03:47:19 +00:00
OpenClaw Sub-agent 184fc53e38 feat(ui): responsive file list with mobile cards & fix video preview reset
- Add responsive card layout for mobile (<640px) as alternative to desktop table
- Desktop: table hidden below sm breakpoint, improved col widths
- Mobile: each file as a card with filename, metadata grid (2x2), public status, actions
- Keep table headers accessible via <caption>
- Fix video seek-bar reset bug: change <template x-if> to x-show so
  video/audio elements stay mounted and playback state isn't lost
  on Alpine re-render
2026-07-15 13:43:02 +10:00
github-actions[bot] 46aebffad2 chore: update manifest image to 1.0.350 [skip ci] 2026-07-15 01:47:05 +00:00
OpenClaw Sub-agent ee98e18302 fix: file upload defaults private, internal network access, video inline
- _save_uploaded_file: keep is_public=False by default (no auto-public)
- PublicFileView: internal network (192.168/10/172.16-31) serves any file;
  external only serves is_public=True files
- Video files: Content-Disposition inline for browser playback
- DRF API: fix misleading error message (files not files[])
2026-07-15 11:40:53 +10:00
dependabot[bot]andGitHub 67cde4169c build(deps): bump echarts from 5.6.0 to 6.1.0
Bumps [echarts](https://github.com/apache/echarts) from 5.6.0 to 6.1.0.
- [Release notes](https://github.com/apache/echarts/releases)
- [Commits](https://github.com/apache/echarts/compare/5.6.0...6.1.0)

---
updated-dependencies:
- dependency-name: echarts
  dependency-version: 6.1.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-30 17:28:54 +00:00
github-actions[bot] 2dff02cd03 chore: update manifest image to 1.0.345 [skip ci] 2026-06-23 06:31:12 +00:00
junv 647ce87359 Add price monitor toggle 2026-06-23 16:25:59 +10:00
github-actions[bot] a15d79f12f chore: update manifest image to 1.0.341 [skip ci] 2026-06-13 23:09:53 +00:00
junvandGitHub cdae61ae4c Merge pull request #94 from wahyd4/fix/invest-report-tag-alias
fix: show legacy investment reports on invest dashboard
2026-06-14 09:05:48 +10:00
OpenClaw Sub-agent e87b714f84 fix: show legacy investment reports on invest dashboard 2026-06-14 09:04:43 +10:00
github-actions[bot] 8a3882412b chore: update manifest image to 1.0.339 [skip ci] 2026-06-13 22:57:24 +00:00
junvandGitHub 1fa81a1cdc Merge pull request #93 from wahyd4/feat/invest-chart-value-mode
feat: add invest chart value mode
2026-06-14 08:50:26 +10:00
OpenClaw Sub-agent 526868630f feat: add invest chart value mode 2026-06-14 08:47:50 +10:00
github-actions[bot] 0023fc1071 chore: update manifest image to 1.0.337 [skip ci] 2026-06-13 22:27:47 +00:00
junvandGitHub 2dadf8718f Merge pull request #92 from wahyd4/feat/invest-agent-performance-main
feat: add cashflow-adjusted invest dashboard to main
2026-06-14 08:22:48 +10:00
OpenClaw Sub-agent f7a3d228c1 feat: add cashflow-adjusted invest dashboard 2026-06-14 08:19:44 +10:00
github-actions[bot] de893d47cf chore: update manifest image to 1.0.334 [skip ci] 2026-05-23 00:18:55 +00:00
junv 466b9c1fcb add testing feature 2026-05-23 10:15:09 +10:00
junv 227bc9b1de Update price monitor settings 2026-05-23 10:15:09 +10:00
github-actions[bot] 4721eec917 chore: update manifest image to 1.0.331 [skip ci] 2026-05-17 10:39:51 +00:00
junvandCopilot d99a362a37 jbot: keep avatar thinking during TTS and protect history storage
- Use a thinking/running face while speech audio is being generated
- Switch to speaking only when playback actually begins
- Add quota-safe history persistence that strips audio from older entries
  instead of crashing localStorage writes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 20:34:57 +10:00
junvandCopilot 4b2a4621fb jbot: delay TTS status and strip markdown from speech
- Show a dedicated '生成语音中…' state while TTS is being generated
- Only switch to '播放中…' once audio is actually ready/playing
- Strip markdown formatting from the spoken text while preserving the raw
  assistant message in the chat UI
- Render assistant messages as lightweight markdown in the bubble view

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 20:34:57 +10:00
github-actions[bot] a06be33896 chore: update manifest image to 1.0.329 [skip ci] 2026-05-17 04:17:21 +00:00
junv dcb31838d9 Update UI 2026-05-17 14:13:38 +10:00
junvandCopilot 21fd058d46 jbot: fix voice button stuck state + add global fallback handlers
Fixes:
1. Race condition: after await startRec(), check isSpaceDownRef again —
   fast-tap released before mic ready was leaving phase stuck at 'listening'
2. Global window mouseup/touchend/touchcancel listeners as fallback so
   releasing outside the button boundary always triggers handleSpaceUp
3. Show ✕ cancel button during ALL non-idle phases (was only thinking/speaking)
4. handleCancel now stops the recorder if called during listening/acquiring
5. Safety auto-cancel timeout: if stuck in 'listening' > 60s, auto-stop
6. Add stopRec to handleSpaceDown dep array

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:38 +10:00
junvandCopilot 2c41b46da0 jbot: cancel button + abort + better Hermes error message
- VoiceButton gets a red ✕ cancel button that appears during thinking/speaking
- AbortController per pipeline run — ✕ aborts the in-flight apiChat fetch,
  stops TTS playback, and returns to idle cleanly
- Hermes connection-refused error shows readable message instead of raw stack
- apiChat() accepts optional AbortSignal (4th arg), passed from App.jsx
- handleSpaceUp dep array updated

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot bb3fe1b554 jbot: disable local memory in Hermes Agent mode
In Hermes mode JBOT is a pure voice UI — memory is owned by the Agent:
- Backend: sys prompt excludes local memories when backend_mode=hermes
- Backend: config_view now returns backendMode so the frontend can adapt
- Frontend: App.jsx loads backendMode on mount via apiConfig()
- Frontend: memories not passed to apiChat() when isHermesMode
- Frontend: memory extraction (apiExtractMemory) skipped when isHermesMode
- Frontend: MemoryPanel hidden in Hermes mode; replaced by subtle
  '🧠 Hermes Agent 模式 · 记忆由 Agent 管理' badge

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 76603425a5 jbot: add Hermes Agent backend mode
- New DB fields: hermes_url, hermes_api_key, backend_mode (openrouter/hermes)
- Migration 0002 adds the three fields to JbotApiConfig
- _do_hermes_chat(): streams /v1/chat/completions from Hermes Agent,
  collects hermes.tool.progress SSE events, returns (final_text, tool_calls)
- chat_view branches on backend_mode; Hermes response includes toolCalls list
- Settings page: new '🧠 AI 后端模式' section — radio to toggle mode,
  conditional Hermes URL + API key fields, LLM model field moved under
  OpenRouter mode
- ChatPanel: ToolCallsList component renders collapsible '🔧 Hermes 使用了
  N 个工具' widget before the bot reply — tool calls are NOT spoken via TTS
- CSS: .tool-calls, .tool-calls-* styles for the tool progress widget

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot d944c3ecf9 jbot: remove right-footer wrapper — vbtn-wrap is direct flex child
right-footer served no purpose after hint text was removed. vbtn-wrap
is now a direct flex child of right-col: white bar at the bottom on
desktop, position:fixed on mobile — no stray white boxes anywhere.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot c60b1c3bd9 jbot: remove white background from chat panel — blends with right column
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 7cd36ad81b jbot: remove hint text from bottom footer — keep only the voice button
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 85da425c38 jbot: anchor voice button to bottom via unified right-footer
- Merged hint text + VoiceButton into a single .right-footer div
  (flex column, white bg, border-top) so they form one solid footer bar
  visually anchored to the bottom of the right column — no more floating
- .right-col gets min-height:0 so flex children can shrink properly
- .vbtn-wrap is now a transparent width:100% wrapper inside right-footer
  (desktop); mobile keeps its own fixed-position overlay
- .right-footer { display: none } on mobile (fixed vbtn-wrap takes over)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 90ebd68511 jbot: fix Settings scrollbar + remove redundant history link
- Remove 'overflow:hidden' from html/body in index.css — it was blocking
  scroll on all pages (Settings, History). The chat page is self-contained
  via .app {height:100dvh; overflow:hidden} so body lock is unnecessary.
  .app reverted back to height:100dvh (self-sufficient, not body-relative)
- Remove duplicate 历史记录 link from the right-column hint bar; it
  already appears in the ChatPanel header, so three links was redundant

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 3cbbaac083 jbot: reorganize layout — memory+hint to right, interval to settings
- Move MemoryPanel from bottom of left column to top of right column
  (above chat messages, with a clean border-bottom separator)
- Move hint text ('按住 Space 或按钮说话 · 音色') from left column
  to a compact bar on the right column between chat and voice button;
  hidden on mobile to save space
- Remove 切换间隔 slider from left column; add it to Settings page
  under 🤖 机器人身份 section with proper slider+value layout
- Add switchIntervalMs:10000 to DEFAULT_SETTINGS in settingsStore so
  the value persists across sessions; App.jsx reads it on mount
- Left column is now much leaner: face + interaction buttons + micro
  expression grid + auto-rotate toggle only

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 4870e1a6f4 jbot: hide left-col scrollbar + add home button
- Left column: scrollbar-width:none + ::-webkit-scrollbar:none to
  hide the scrollbar chrome while keeping overflow-y:auto for tall
  viewports; tightened padding (14→10px top, 20→14px bottom) and
  panel/section gaps (14→10px, 12→8px, h3 margin 7→5px) to help
  content fit without scrolling on typical laptop heights
- Header: added 🏠 home anchor linking to '/' (Links homepage) next
  to the JBOT logo; styled as a small icon button

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 7f55aec007 jbot: fix no-scroll viewport layout
- index.css targeted #root but app mounts to #jbot-root — viewport
  height was never constrained, causing full page scroll
- Fix: rename #root → #jbot-root, add html/body {height:100%;overflow:hidden}
- .app now uses height:100% relative to #jbot-root instead of
  duplicate 100dvh, so the layout is a true single-screen SPA

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 1efcebfd9b jbot: fix voice button visibility - chat-panel height overflow
.chat-panel was using height:100% which consumed all right-col space,
pushing VoiceButton out of view (clipped by overflow:hidden).

Fix: switch .chat-panel to flex:1 + min-height:0 so it participates
in flex layout and leaves room for the VoiceButton below it.

Also moved VoiceButton component outside App() to avoid React
component-identity churn on every render.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot cdb7efd61d jbot: add WeChat-style hold-to-talk voice button
- Added VoiceButton component (inline in App.jsx) with full
  mousedown/mouseup/mouseleave + touchstart/touchend/touchcancel support
- Shows context-aware label: 🎙 按住说话 /  准备中 / 🔴 松开发送 / ⚙ 思考中 / 🔊 播放中
- Pulsing red animation while recording, amber pulse while acquiring mic
- Disabled (gray) while AI is thinking or speaking
- Desktop: pill button docked at bottom of chat panel (max-width 380px)
- Mobile: fixed to bottom of viewport with safe-area-inset support,
  height 60px for easy thumb tap; right-col padded so messages aren't hidden
- Updated hint text and listening status to be input-agnostic

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot fd1d9db753 fix(jbot): ignore normal WebSocket close frame in on_error
Volcengine ASR server sends a WS close frame (opcode=8, code 1000,
'finish last sequence') after receiving the final audio chunk.
websocket-client fires on_error with this frame data instead of
(or in addition to) on_close — treat opcode=8 and 'already closed'
as normal termination, not an error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00
junvandCopilot 2d6d4b09fc fix(jbot): replace WebSocketApp.send_binary with ws.send(data, OPCODE_BINARY)
WebSocketApp callbacks receive the WebSocketApp object, not WebSocket.
WebSocketApp has no send_binary() method — must use:
  ws.send(data, ws_module.ABNF.OPCODE_BINARY)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-17 14:13:37 +10:00