feat: add attachment button and mobile UI enhancements (v2.1)

This commit is contained in:
leeyj
2026-04-19 23:22:35 +09:00
parent 3d3709371d
commit ff9d75c26f
9 changed files with 97 additions and 41 deletions
+10
View File
@@ -45,6 +45,11 @@
* **고속 워크플로우 (Instant Edit)**: 메모 카드 위에 마우스를 올리고 `e`를 누르면 즉시 수정 모드 진입. 모달 클릭의 피로감을 제로로 만듭니다.
* **드래그 앤 드롭 링크**: 메모 카드를 작성기(Composer)로 드래그하여 즉시 참조 링크를 삽입하세요.
### 🛠️ 패치 노트 (2026-04-19)
* **파일 첨부 접근성 개선**: 지식 작성기(Composer)에 명시적인 파일 첨부(📎) 버튼을 추가했습니다. (드래그 앤 드롭과 병행 가능)
* **모바일 UX 최적화**: 모바일 기기에서도 조작이 편리하도록 "파일추가" 텍스트 레이블을 추가했습니다.
* **다국어 지원 안정화**: 첨부파일 관련 툴팁 및 레이블에 한/영 i18n을 적용했습니다.
---
## 🆚 memos vs 뇌사료 (Comparison)
@@ -127,6 +132,11 @@ We provide a security model where user data is practically undecipherable. Built
- **Instant Edit (e-key)**: Hover over a memo and press `e` to jump straight into editing mode. Zero-click productivity.
- **Drag & Drop Workflow**: Drag memo cards into the composer to instantly insert a semantic reference.
### 🛠️ Patch Notes (2026-04-19)
- **Improved Attachment Accessibility**: Added a dedicated Attach File (📎) button to the Composer.
- **Mobile UI Optimization**: Added an "Add File" text label next to the icon on mobile devices for better touch usability.
- **I18n Stabilization**: Implemented full Korean/English translation for all attachment-related UI elements.
---
## 🆚 memos vs Brain Dogfood (Comparison)
-13
View File
@@ -1,13 +0,0 @@
# 버그 리포트: Stable 레포지토리 brain.py 누락
## 버그 내용
- `brain_dogfood_stable` 레포지토리에 Flask 애플리케이션의 진입점인 `brain.py` 파일이 누락되어 서버 구동이 불가능했던 현상.
## 조치 사항
1. 루트 디렉토리의 `brain.py` 파일을 `brain_dogfood_stable` 디렉토리로 복사.
2. Git commit 및 원격 저장소(`origin main`)에 푸시 완료.
3. `tools/sync_stable.py``tools/final_audit.py`에서 `brain.py`를 제외 목록에서 삭제하여 향후 자동 동기화되도록 조치.
## 향후 주의사항
- 배포용 레포지토리(stable) 업데이트 시, 실행에 필수적인 진입점 파일(`brain.py`)이 포함되어 있는지 반드시 확인해야 함.
- 동기화 도구(`sync_stable.py`)의 제외 목록(`EXCLUDE_FILES`)을 수정할 때는 서비스 코어 파일이 포함되지 않도록 주의 필요.
@@ -0,0 +1,28 @@
# 💡 지식 작성기(Composer) 파일 첨부 버튼 누락 및 모바일 UI 개선
- **날짜**: 2026-04-19
- **작성자**: Antigravity (AI Agent)
## 1. 개요
지식 작성기(Composer)에서 드래그 앤 드롭 방식 외에 파일을 직접 선택할 수 있는 물리적 버튼이 없어 접근성이 낮음. 특히 모바일 환경에서 아이콘만 있을 경우 조작 오탐점이 높고 가시성이 떨어지는 문제 발생.
## 2. 조치 사항
- **UI 개선**:
- `templates/components/composer.html` 내부에 클립(📎) 아이콘 버튼 및 숨겨진 `input[type="file"]` 추가.
- 모바일 해상도(768px 이하)에서만 나타나는 텍스트 레이블("파일추가") 추가.
- **다국어 지원(i18n)**:
- `static/locales/ko.json`, `en.json``composer_attach_file`, `composer_attach_label` 키 추가.
- **로직 최적화**:
- `static/js/editor.js`: 드롭 이벤트와 버튼 클릭 이벤트를 공통으로 처리하기 위한 `handleFiles()` 함수 추출.
- `static/js/components/ComposerManager.js`: 신규 버튼과 파일 인풋 간의 이벤트 바인딩 처리.
- **스타일링**:
- `static/css/layout.css`: 기본 레이블 숨김 처리.
- `static/css/mobile.css`: 모바일 환경에서 레이블 노출 및 터치 영역 최적화.
## 3. 결과 및 검증
- 홈 서버 배포 완료.
- 데스크톱: 툴팁 지원 및 깔끔한 아이콘 UI 확인.
- 모바일: 텍스트 레이블 노출로 조작 편의성 증대 확인.
## 4. 향후 주의 사항
- `action-btn` 스타일 변경 시 모바일에서의 `min-width`, `min-height``44px` 이상 유지되는지 확인 필요 (Apple/Google UI 가이드라인 준수).
+1
View File
@@ -104,6 +104,7 @@
}
.action-btn:hover { background: rgba(184, 59, 94, 0.8); }
.btn-label { display: none; }
.ai-btn:hover { background: var(--ai-accent); color: white; }
/* Memo Footer Metadata Styling */
+1
View File
@@ -33,6 +33,7 @@
.memo-actions { opacity: 1 !important; bottom: 8px; right: 8px; }
.action-btn { padding: 8px 12px; font-size: 1rem; min-width: 44px; min-height: 44px; display: flex; align-items: center; justify-content: center; }
.btn-label { display: inline; margin-left: 5px; font-size: 0.9rem; font-weight: bold; }
.modal-content { width: 95%; max-height: 90vh; border-radius: 12px; }
.tag-badge, .group-badge { padding: 4px 10px; font-size: 0.85rem; }
+15 -1
View File
@@ -28,7 +28,9 @@ export const ComposerManager = {
foldBtn: document.getElementById('foldBtn'),
discardBtn: document.getElementById('discardBtn'),
deleteBtn: document.getElementById('deleteMemoBtn'), // NEW
categoryBar: document.getElementById('composerCategoryBar')
categoryBar: document.getElementById('composerCategoryBar'),
attachBtn: document.getElementById('attachBtn'),
fileInput: document.getElementById('composerFileInput')
};
if (!this.DOM.composer || !this.DOM.trigger) return;
@@ -71,6 +73,18 @@ export const ComposerManager = {
this.DOM.encryptionToggle.onclick = () => this.toggleEncryption();
this.initShortcutHint();
// 💡 파일 첨부 버튼 연동
if (this.DOM.attachBtn && this.DOM.fileInput) {
this.DOM.attachBtn.onclick = () => this.DOM.fileInput.click();
this.DOM.fileInput.onchange = (e) => {
const files = e.target.files;
if (files.length > 0) {
EditorManager.handleFiles(files);
e.target.value = ''; // 같은 파일 다시 올릴 수 있게 초기화
}
};
}
// 2. 자동 임시저장 및 키보드 리스너 등록
this.draftTimer = setInterval(() => this.saveDraft(), 3000);
ComposerDraft.checkRestore((draft) => this.restoreDraft(draft));
+36 -27
View File
@@ -147,37 +147,46 @@ export const EditorManager = {
if (!files || files.length === 0) return;
// 에디터가 닫혀있다면 상위에서 열어줘야 함
onDropComplete(true);
if (onDropComplete) onDropComplete(true);
for (let file of files) {
try {
const data = await API.uploadFile(file);
if (data.url) {
const filename = data.url.split('/').pop();
const isImg = ['png','jpg','jpeg','gif','webp','svg'].includes(data.ext?.toLowerCase());
const name = data.name || 'file';
// Ensure editor is focused before inserting
this.editor.focus();
if (isImg) {
this.editor.exec('addImage', { altText: name, imageUrl: data.url });
}
// 공통: 첨부 파일 목록에 추가 및 UI 갱신
this.attachedFiles.push({
filename: filename,
original_name: name,
file_type: file.type
});
this.sessionFiles.add(filename); // 세션 트래킹 추가
this.refreshAttachmentUI();
}
} catch (err) { console.error(err); }
}
await this.handleFiles(files);
});
},
/**
* 다중 파일을 처리(업로드 및 UI 반영)함
*/
async handleFiles(files) {
if (!files || files.length === 0) return;
for (let file of files) {
try {
const data = await API.uploadFile(file);
if (data.url) {
const filename = data.url.split('/').pop();
const isImg = ['png','jpg','jpeg','gif','webp','svg'].includes(data.ext?.toLowerCase());
const name = data.name || 'file';
// Ensure editor is focused before inserting
this.editor.focus();
if (isImg) {
this.editor.exec('addImage', { altText: name, imageUrl: data.url });
}
// 공통: 첨부 파일 목록에 추가 및 UI 갱신
this.attachedFiles.push({
filename: filename,
original_name: name,
file_type: file.type
});
this.sessionFiles.add(filename); // 세션 트래킹 추가
this.refreshAttachmentUI();
}
} catch (err) { console.error('[Editor] File process error:', err); }
}
},
getAttachedFilenames() {
return this.attachedFiles.map(f => f.filename);
},
+2
View File
@@ -23,6 +23,8 @@
"composer_save": "메모 저장",
"composer_discard": "취소 (삭제)",
"composer_encrypt": "암호화",
"composer_attach_file": "파일 첨부",
"composer_attach_label": "파일추가",
"composer_password": "비밀번호",
"tooltip_fold": "창 접기 (내용 보존)",
+4
View File
@@ -17,6 +17,10 @@
<input type="text" id="memoGroup" data-i18n-placeholder="composer_group" class="meta-field" style="width: 120px;">
<input type="text" id="memoTags" data-i18n-placeholder="composer_tags" class="meta-field" style="flex: 1;">
<button type="button" id="encryptionToggle" class="action-btn" data-i18n-title="composer_encrypt" style="height:34px; padding:0 10px;">🔓</button>
<button type="button" id="attachBtn" class="action-btn" data-i18n-title="composer_attach_file" style="height:34px; padding:0 10px;">
📎 <span class="btn-label" data-i18n="composer_attach_label"></span>
</button>
<input type="file" id="composerFileInput" multiple style="display: none;">
<input type="password" id="memoPassword" data-i18n-placeholder="composer_password" class="meta-field" style="width: 120px; display: none;">
</div>