更新 shenyan-app: HealthKit+WeatherKit+冰箱贴+pare+三区布局
- HealthKitBridge.m: 自定义原生模块,心率/步数/睡眠采集 - WeatherKitBridge.swift/m: 天气模块,当前+每日+每小时预报 - pare.py: 身体数据陡度监控,阈值告警→inbox+冰箱贴 - cyberboss: 5-20分钟随机唤醒,pare→decide→push - 冰箱贴: 纸质感卡片,黑字多级透明度,独立输入框 - 首页三区无视觉布局: 左门/中记录/右冰箱 - 端口3003→3004(VS Code占用) - 聊天屏锚定底部滚动 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -3,16 +3,17 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { sendAPNs } = require('./apns');
|
||||
|
||||
const PORT = 3003;
|
||||
const PORT = 3004;
|
||||
const DATA_DIR = path.join(require('os').homedir(), '.shenyan-bridge');
|
||||
const INBOX = path.join(DATA_DIR, 'inbox.jsonl');
|
||||
const OUTBOX = path.join(DATA_DIR, 'outbox.jsonl');
|
||||
const PUSHBOX = path.join(DATA_DIR, 'pushbox.jsonl');
|
||||
const NOTES = path.join(DATA_DIR, 'notes.jsonl');
|
||||
const DEVICE_TOKEN_FILE = path.join(DATA_DIR, 'device-token.txt');
|
||||
|
||||
// ensure data dir and files
|
||||
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true });
|
||||
[INBOX, OUTBOX, PUSHBOX].forEach(f => { if (!fs.existsSync(f)) fs.writeFileSync(f, ''); });
|
||||
[INBOX, OUTBOX, PUSHBOX, NOTES].forEach(f => { if (!fs.existsSync(f)) fs.writeFileSync(f, ''); });
|
||||
|
||||
function json(res, data, code = 200) {
|
||||
res.writeHead(code, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
@@ -190,6 +191,48 @@ const server = http.createServer((req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- fridge notes ----
|
||||
if (req.method === 'GET' && req.url === '/notes') {
|
||||
json(res, readJSONL(NOTES));
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'POST' && req.url === '/notes/add') {
|
||||
let body = '';
|
||||
req.on('data', c => body += c);
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const { title, body: noteBody } = JSON.parse(body);
|
||||
if (!title && !noteBody) return json(res, { error: 'empty' }, 400);
|
||||
const entry = { id: getNextId(NOTES), title: title || '', body: noteBody || '', time: Date.now(), replies: [] };
|
||||
appendJSONL(NOTES, entry);
|
||||
json(res, { ok: true, id: entry.id });
|
||||
} catch (e) { json(res, { error: e.message }, 400); }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method === 'POST' && req.url === '/notes/reply') {
|
||||
let body = '';
|
||||
req.on('data', c => body += c);
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const { noteId, text } = JSON.parse(body);
|
||||
if (!text) return json(res, { error: 'empty' }, 400);
|
||||
const notes = readJSONL(NOTES);
|
||||
const idx = notes.findIndex(n => n.id === noteId);
|
||||
if (idx < 0) return json(res, { error: 'not found' }, 404);
|
||||
notes[idx].replies = notes[idx].replies || [];
|
||||
notes[idx].replies.push({ text, time: Date.now() });
|
||||
writeJSONL(NOTES, notes);
|
||||
// also notify inbox so 沈晏 sees the reply
|
||||
appendJSONL(INBOX, { id: getNextId(INBOX), type: 'fridge_reply', text: `[冰箱贴回复] ${notes[idx].title}: ${text}`, time: Date.now(), status: 'pending' });
|
||||
json(res, { ok: true });
|
||||
} catch (e) { json(res, { error: e.message }, 400); }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(404); res.end('not found');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user