release: ship v1.5.0 with on-device writing suggestions
Adds next-sentence suggestions while typing, weekly input insights and a personal phrase memory to the desktop app, and fixes custom instructions so they process the text instead of inserting the instruction's own wording. Local model requests are now bounded and individually cancellable. Bumps the product version to 1.5.0 (Android/iOS build 1050000), refreshes the landing and web download links, and records the new INPUT feature rows and the open verification gaps in the infrastructure map.
This commit is contained in:
parent
99f06c253c
commit
5c11ee2fde
104 changed files with 14410 additions and 174 deletions
|
|
@ -39,6 +39,7 @@ import { formatRecordingTime, formatRecordingTimeUnit, formatNumber, getDateKey
|
|||
import { BindingKeycaps } from '../components/keybinding/Keycap'
|
||||
import { useKeyBindingMap } from '../hooks/useKeyBindingMap'
|
||||
import { FileDropZone } from '../components/FileDropZone'
|
||||
import { useInputInsights } from '../hooks/useInputInsights'
|
||||
import type { StatsSummary, HistoryEntry, CaptionState, LicenseTier, UsageQuota } from '@d3ro/core/types'
|
||||
|
||||
/** 카드 섹션 헤더 (제목 + 우측 액세서리) — 헤어라인 하단 구분 */
|
||||
|
|
@ -103,6 +104,7 @@ export function DashboardPage(): React.ReactElement {
|
|||
const [llmModel, setLlmModel] = useState<string | null>(null)
|
||||
const bindingMap = useKeyBindingMap()
|
||||
const dictationBinding = bindingMap?.dictation[0] ?? null
|
||||
const inputInsights = useInputInsights(7)
|
||||
const [captionState, setCaptionState] = useState<CaptionState>('inactive')
|
||||
const [audioLevel, setAudioLevel] = useState(0)
|
||||
const audioDecayRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
|
@ -642,6 +644,57 @@ export function DashboardPage(): React.ReactElement {
|
|||
</Box>
|
||||
</MetalCard>
|
||||
|
||||
{/* ── 5b. 입력 인사이트 (주간) ──────────────────── */}
|
||||
{inputInsights.telemetry?.enabled ? (
|
||||
<MetalCard sx={{ p: 2.75, mt: 3 }}>
|
||||
<CardHeader
|
||||
title={t('input.insights.title', { days: inputInsights.summary?.days ?? 7 })}
|
||||
icon={<BarChart3 size={16} />}
|
||||
right={
|
||||
<PhosphorText variant="meta" sx={{ color: d3roPalette.text.dimLabel }}>
|
||||
{t('input.insights.keystrokes')} {formatNumber(inputInsights.summary?.totals.keystrokes ?? 0)}
|
||||
</PhosphorText>
|
||||
}
|
||||
/>
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 2 }}>
|
||||
{[
|
||||
{
|
||||
label: t('input.insights.clicks'),
|
||||
value: formatNumber(inputInsights.summary?.totals.clicks ?? 0),
|
||||
},
|
||||
{
|
||||
label: t('input.insights.words'),
|
||||
value: formatNumber(inputInsights.summary?.totals.words ?? 0),
|
||||
},
|
||||
{
|
||||
label: t('input.insights.sentences'),
|
||||
value: formatNumber(inputInsights.summary?.totals.sentences ?? 0),
|
||||
},
|
||||
{
|
||||
label: t('input.insights.mouseDistance'),
|
||||
value: `${inputInsights.summary?.averages.mouseDistanceMeters ?? 0}${t('input.insights.perDayMeters')}`,
|
||||
},
|
||||
{
|
||||
label: t('input.insights.phrases'),
|
||||
value: formatNumber(inputInsights.summary?.phraseCount ?? 0),
|
||||
},
|
||||
].map((item) => (
|
||||
<Box
|
||||
key={item.label}
|
||||
sx={{ flex: '1 1 140px', minWidth: 140, px: 1.5, py: 1, borderRadius: d3roRadius.xs }}
|
||||
>
|
||||
<PhosphorText variant="label" sx={{ color: d3roPalette.text.dimLabel }}>
|
||||
{item.label}
|
||||
</PhosphorText>
|
||||
<PhosphorText variant="value" sx={{ color: d3roPalette.text.primary }}>
|
||||
{item.value}
|
||||
</PhosphorText>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</MetalCard>
|
||||
) : null}
|
||||
|
||||
{/* ── 6. 파일 전사 드롭존 ──────────────────────────── */}
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<FileDropZone />
|
||||
|
|
|
|||
|
|
@ -2,16 +2,21 @@
|
|||
// Local RAG Knowledge Base & Semantic Memory Intelligence
|
||||
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import { Box, TextField, IconButton, Tooltip, LinearProgress } from '@mui/material'
|
||||
import { Box, TextField, IconButton, Tooltip, LinearProgress, Tab, Tabs } from '@mui/material'
|
||||
import { Plus, Trash2, RefreshCw, Search, Send, FileText, Database, Sparkles } from 'lucide-react'
|
||||
import { MetalCard, PhosphorText, Led, PhysicalButton, DoubleBezelCard, TactileBadge } from '@d3ro/ui/components/ds'
|
||||
import { PageHeader, EmptyStateCard } from '../components/shared'
|
||||
import { InputInsightsView } from '../components/input-insights/InputInsightsView'
|
||||
import { isImeComposingEvent } from '../utils/keyboard'
|
||||
import { d3roPalette, d3roFontSans, d3roTypo, d3roRadius, d3roShadow } from '@d3ro/ui/theme'
|
||||
import { useI18n } from '@d3ro/i18n'
|
||||
import type { RAGDocument, RAGQueryResult, RAGIndexProgress } from '@d3ro/core/types'
|
||||
|
||||
type KnowledgeView = 'kb' | 'insights'
|
||||
|
||||
export function KnowledgeBasePage(): React.ReactElement {
|
||||
// 이 화면은 두 축을 가진다: 수집된 지식 문서(RAG)와 자동 수집된 입력 데이터 통계.
|
||||
const [view, setView] = useState<KnowledgeView>('kb')
|
||||
const { t } = useI18n()
|
||||
const [documents, setDocuments] = useState<RAGDocument[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
|
@ -128,6 +133,32 @@ export function KnowledgeBasePage(): React.ReactElement {
|
|||
}
|
||||
/>
|
||||
|
||||
<Tabs
|
||||
value={view}
|
||||
onChange={(_event, next: KnowledgeView) => setView(next)}
|
||||
sx={{
|
||||
mb: 2.5,
|
||||
minHeight: 38,
|
||||
borderBottom: `1px solid ${d3roPalette.border.subtle}`,
|
||||
'& .MuiTab-root': {
|
||||
minHeight: 38,
|
||||
py: 0.5,
|
||||
textTransform: 'none',
|
||||
color: d3roPalette.text.inactive,
|
||||
fontSize: d3roTypo.label.size,
|
||||
'&.Mui-selected': { color: d3roPalette.accent.main }
|
||||
},
|
||||
'& .MuiTabs-indicator': { bgcolor: d3roPalette.accent.main, height: 2 }
|
||||
}}
|
||||
>
|
||||
<Tab value="kb" label={t('input.kbView.knowledge')} />
|
||||
<Tab value="insights" label={t('input.kbView.insights')} />
|
||||
</Tabs>
|
||||
|
||||
{view === 'insights' ? (
|
||||
<InputInsightsView />
|
||||
) : (
|
||||
<>
|
||||
{/* Indexing Progress Card */}
|
||||
{indexProgress && (
|
||||
<MetalCard sx={{ mb: 2.5, p: 2.5 }}>
|
||||
|
|
@ -411,6 +442,8 @@ export function KnowledgeBasePage(): React.ReactElement {
|
|||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue