智能助手网
标签聚合 On

/tag/On

linux.do · 2026-04-18 23:02:41+08:00 · tech

非开发出身一直对Obsidian敬而远之,但最近还是入了Obsidian的坑,先说结论就是舒服极了。Vibe coding 都能上手的人Obsidian 几乎也算是0门槛了。 Notion转Obsidian原因有几个: 1、Notion 现在功能越做越臃肿了,仿佛看到了当年使用印象笔记的影子,这个是我不喜欢的 2、教育账户多少感觉存在风险,笔记日益多了,那天挂了就惨了。 3、AI 好用但也贵,不舍得花钱,也不愿意组别人空间 4、 不得不承认很喜欢notion 的强大数据库多维表格功能,唯一还创建了一个套第二大脑笔记系统,非常舒服,但迁移成本很高。用数据库创建的笔记后面迁移到Obsidian 出现大量格式问题。 5、CC大行其道后,感觉.MD格式文件才能顺应AI潮流,我迁移差不多了就出现Karpathy爆款记录笔记方法,也证明了我的判断。 目前没事就折腾obsidian 的格式主题,怎么弄好看。虽然时间花在这些东西上很不值得,因该去记录,但让自己舒服才符合自己使用习惯。 推荐几个好用的插件吧: #主题 : Minimal 强推! 配合Minimal Theme Settings; #AI: Claudian 强推! Obsidian 中直接操作笔记; 导航管理: notebook navigator 强推! 比Obsidian 原生导航好用一万倍的神奇,如果让我只推荐一个插件那必是它了; 附件管理: Custom Attachment Location 强推! obsidian 笔记文件中包含的图片等格式附件有个专门文件夹管理,这个工具就是随笔记一起管理附件好用 多端同步: Remotely Save 强推! 配合云端工具,实现多端同步 4 个帖子 - 4 位参与者 阅读完整话题

linux.do · 2026-04-18 22:23:28+08:00 · tech

(() => { // 如果之前跑过,先停掉旧脚本 if (window.__autoScrollCtl?.stop) { window.__autoScrollCtl.stop(); } const STEP_PX = 260; // 每次下滑距离 const INTERVAL_MS = 1500; // 每次间隔,约为之前的 1/3 const SMOOTH = true; // 是否平滑滚动 const MAX_IDLE_TICKS = 20; // 连续多少次几乎没动就认为到底了 const scroller = document.scrollingElement || document.documentElement; let timer = null; let running = false; let lastTop = scroller.scrollTop; let idleTicks = 0; function tick() { const before = scroller.scrollTop; window.scrollBy({ top: STEP_PX, left: 0, behavior: SMOOTH ? "smooth" : "auto", }); setTimeout(() => { const after = scroller.scrollTop; const moved = Math.abs(after - before); if (moved < 5) { idleTicks += 1; } else { idleTicks = 0; } lastTop = after; if (idleTicks >= MAX_IDLE_TICKS) { console.log("[auto-scroll] 可能已经到底,自动暂停"); pause(); } }, Math.min(900, Math.max(300, INTERVAL_MS - 200))); } function start() { if (running) return; running = true; timer = setInterval(tick, INTERVAL_MS); console.log( `[auto-scroll] 已启动: ${STEP_PX}px / ${INTERVAL_MS}ms` ); } function pause() { if (!running) return; clearInterval(timer); timer = null; running = false; console.log("[auto-scroll] 已暂停"); } function resume() { if (running) return; idleTicks = 0; start(); } function stop() { clearInterval(timer); timer = null; running = false; idleTicks = 0; console.log("[auto-scroll] 已停止"); } window.__autoScrollCtl = { start, pause, resume, stop, status() { return { running, top: scroller.scrollTop, height: scroller.scrollHeight, viewport: window.innerHeight, stepPx: STEP_PX, intervalMs: INTERVAL_MS, idleTicks, }; }, }; start(); })(); 1、用在哪? 比如我看一篇长帖子、或者文章、或者小说,不想自己动手了,可以让浏览器自己下滚 2、怎么用 打开浏览器控制台,找到 console,代码贴进去回车就好了 暂停: __autoScrollCtl.pause() 继续: __autoScrollCtl.resume() 停止: __autoScrollCtl.stop() 查看状态: __autoScrollCtl.status() 5 个帖子 - 4 位参与者 阅读完整话题

linux.do · 2026-04-18 22:05:59+08:00 · tech

<script setup> import { ref } from 'vue'; import { onLoad } from '@dcloudio/uni-app'; import request from '@/utils/request'; import { getFiles, getRandomFile, getFileArray } from '@/utils/yangUtils'; const imageSuffix = ['png', 'jpg', 'jpeg']; const videoSuffix = ['mp4', 'avi', 'mov']; const imageNum = ref(0); const videoNum = ref(0); // 获取父级传入的数据 const props = defineProps(["modelValue", "limit", "message", "isDelete", "isAdd"]); // 定义 emits const emit = defineEmits(['update:modelValue']); // const listData = ref(["https://yxdjpw.oss-cn-beijing.aliyuncs.com/uploadDefault/20260417192542-d3ea46.png", "https://yxdjpw.oss-cn-beijing.aliyuncs.com/uploadDefault/20260417194449-bfb1ba.mp4"]); const listData = ref([]) const onSelectFile = async () => { let result; // #ifdef APP || APP-PLUS || MP-WEIXIN || MP-TOUTIAO || MP-LARK || MP-JD || MP-HARMONY || MP-XHS result = await uni.chooseMedia({ count: props.limit || 9, mediaType: ['image', 'video'], sourceType: ['album', 'camera'], maxDuration: '30s' }) // #endif // #ifdef WEB || H5 result = await uni.chooseFile({ count: props.limit || 9, type: 'all', }) // #endif let arr = []; console.log(result); for (let filePath of result.tempFiles) { // 判断文件是否超出10M if (filePath.size > 10 * 1024 * 1024) { uni.showModal({ title: '提示', content: '文件大小不能超过10M!', showCancel: true, }) return; } if (imageSuffix.includes(filePath.name.replaceAll('"', '').split('.').pop()?.toLowerCase())) { imageNum.value++; } else if (videoSuffix.includes(filePath.name.replaceAll('"', '').split('.').pop()?.toLowerCase())) { // 判断之前有没有上传过视频或者图片 if (imageNum.value >= 1 || videoNum.value >= 1) { uni.showModal({ title: '提示', content: '图片和视频不能同时上传,并且视频只能上传一个!', showCancel: true, }) return; } videoNum.value++; } const data = await request("/upload/uploadFile", filePath.path, "post"); arr.push(getFileArray(data)[0]); } listData.value = listData.value.concat(arr); // 将数据返回出去 emit("update:modelValue", listData.value.join(",")); } // 删除 const onDelete = (index) => { // 1. 先拿到要删除的项(必须在 splice 之前拿!) const deletedItem = listData.value[index]; // 2. 判断类型,更新计数 const ext = deletedItem.split('.').pop()?.toLowerCase(); if (imageSuffix.includes(ext)) { imageNum.value--; } else if (videoSuffix.includes(ext)) { videoNum.value--; } // 3. 再删除元素 listData.value.splice(index, 1); // 4. 更新双向绑定 emit("update:modelValue", listData.value.join(",")); } // 查看 const onView = (item) => { } // 类型判断 const isSuffix = () => { return listData.value.some(item => { // 获取后缀(转小写,避免大小写问题) const ext = item.split('.').pop()?.toLowerCase() return videoSuffix.includes(ext) }) } </script> <template> <view> <view class="header"> <view v-if="props.message" class="message">{{ props.message }}</view> <view class="numCount">{{ `${listData.length}/${props.limit || 9}` }}</view> </view> <view class="image-grid" :class="isSuffix() ? 'grid-video' : ''"> <template v-for="(item, index) in listData" :key="index"> <view class="grid-item grid-item-content" :class="isSuffix() ? 'grid-item-video' : ''" @click="onView(item)"> <image v-if="imageSuffix.includes(item.split('.').pop())" class="img" :src="item" mode="aspectFill" /> <video v-else-if="videoSuffix.includes(item.split('.').pop())" class="video" :src="item" /> <view class="grid-item-delete" v-if="(isAdd ?? true)" @click="onDelete(index)"><uni-icons class="icon-delete" type="closeempty" color="#D3D4D6" size="24" /></view> </view> </template> <view class="grid-item icon-add" v-if="(isAdd ?? true) && !isSuffix() && listData.length < (props.limit ?? 9)" @click="onSelectFile()"> <uni-icons type="plusempty" size="60" color="#F1F1F1"></uni-icons> </view> </view> </view> </template> <style lang="scss" scoped> .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; } .image-grid { display: grid; grid-template-columns: repeat(3, 1fr); /* 一行3个,自动均分 */ gap: 20rpx; /* 格子间距 */ box-sizing: border-box; } .grid-item { width: 240rpx; height: 240rpx; } .grid-video { grid-template-columns: repeat(1, 1fr) !important; } .grid-item-video { width: 100%; height: 400rpx; } .numCount { display: flex; flex-direction: row-reverse; font-size: 26rpx; } .img, .video { width: 100%; height: 100%; } .grid-item-content { position: relative; } .icon-add { background: #FFFFFF; line-height: 240rpx; text-align: center; border: 1rpx solid #EEEEEE; border-radius: 6rpx; } .grid-item-delete { position: absolute; top: 0rpx; right: 0rpx; z-index: 1; } </style> 方法解析 文件后端返回的是 /api/upload/xxx.png getFiles 将图片拼接成正常能访问的 比如 http://域名.com/api/upload/xxx.png getRandomFile 是随机访问 getFileArray 是拼接成数组 limit → 最多上传数量 默认为9 message → 提示 比如请上传视频 可不填 isDelete → 是否可以删除重新上传 默认为true isAdd → 是否可以新增,比如有些地方可以直接回显 比如产品的图片,但是你不想让它显示新增的框 可以为false 默认为true // 文件加载 export const getFiles = (url) => { if (!url) { return ""; } if (url.startsWith("http://") || url.startsWith("https://")) { return cleanString(url); } else { return baseURL + cleanString(url); } }; // 随机加载文件 export const getRandomFile = (url) => { if (!url) { return ""; } const urls = url.split(","); const randomIndex = Math.floor(Math.random() * urls.length); const selectedUrl = urls[randomIndex]; if ( cleanString(selectedUrl).startsWith("http://") || cleanString(selectedUrl).startsWith("https://") ) { return cleanString(selectedUrl); } else { return baseURL + cleanString(selectedUrl); } }; export const getFileArray = (url) => { if (!url) { return []; } let baseUrl = []; url.split(",").forEach((v) => { if ( cleanString(v).startsWith("http://") || cleanString(v).startsWith("https://") ) { baseUrl.push(cleanString(v)); } else { baseUrl.push(baseURL + cleanString(v)); } }); console.log(baseUrl); return baseUrl; }; 请求就是 uni.request的请求 你们按照你们自己的改 当前只支持H5 你们需要的话 我会再更新 如果上传了视频就直接独占一行 并且不能上传其他视频和图片 仿照的是朋友圈 其他页面调用就是 比如文件名叫uploadFile.vue吧 import UpLoadFile from '@/components/uploadFile.vue' // 页面代码 <UpLoadFile v-model="form.media" /> 第一次封装 还有些问题 我会持续更新的 1 个帖子 - 1 位参与者 阅读完整话题

linux.do · 2026-04-18 21:51:34+08:00 · tech

旧闻了 iPhone 用户现在也可以在手机上运行 Google 新发布的 Gemma 4 模型了——是真的在手机上运行,断网也能用的那种。 App Store 直接搜 “Google AI Edge Gallery”。打开 APP 后可以选择下载 E2B 或者 E4B。官方显然推荐大家用 E2B 这个更小也更快的版本。 除了文本对话,还有图片识别、语音对话等,甚至还支持 Skills。但是实测知识是 2025 年 1 月以前的。 推理框架用了 iPhone 的 GPU,运行还是相当流畅的。 大家可以试试,日常处理文本翻译之类的应该绰绰有余。 8 个帖子 - 5 位参与者 阅读完整话题

hnrss.org · 2026-04-18 21:10:24+08:00 · tech

Readox is a Chrome extension that reads web pages, PDFs, selections, text files (ie markdown), and text input notes aloud with TTS. You can just play the current page, or save things to a library and queue them up. It highlights as it reads, keeps your place across items, and can OCR scanned PDFs. TTS (for text and PDF) and OCR run on-device, and it works offline after setup. I’m interested on feedback on the library collections as a "playlist-like" functionality, or if most people just want a play button for the current page. Also interested in anything that feels missing or awkward. Thanks! Comments URL: https://news.ycombinator.com/item?id=47815638 Points: 2 # Comments: 0

linux.do · 2026-04-18 21:02:53+08:00 · tech

抽奖主题:抽10个codex free json 账号,导入就可以使用 奖品详情: [奖品1]:[5个codex] [奖品2]:[5个codex] 活动时间: 开始时间: Sat, Apr 18, 2026 9:00 PM CST 截止时间: Sun, Apr 19, 2026 9:00 PM CST 参与方式: 在本贴下回复任意内容即可参与。 抽奖规则: 每位用户仅允许参与一次。 将使用 LINUX DO 抽奖工具 在所有回复中随机抽取中奖者。 注意事项: 本活动将在活动截止时间后关闭回帖,以确保公正性。 中奖者将在活动结束后在本帖公布,并通过论坛站内信由发起人通知领奖方式。 所有规则及抽奖结果由 @kexincolar 及论坛 管理团队 最终解释。 发起人承诺: 作为本次抽奖的发起人 @kexincolar ,我承诺本话题的抽奖活动严格遵守 LINUX DO 社区抽奖规则 。因违反上述规定引发的公平性争议或其他问题,均由我独立承担相应的道德与法律责任。 期待您的积极参与,祝您好运!如有任何疑问,欢迎随时联系 @kexincolar 或论坛 管理团队 。 39 个帖子 - 39 位参与者 阅读完整话题

hnrss.org · 2026-04-18 20:30:16+08:00 · tech

I built LogsGo as a learning project to explore log ingestion, querying, and storage tradeoffs. It’s a small Go-based system where logs come in over gRPC, land in memory first, then flush into local storage and optionally S3-compatible object storage. I also added a simple query language plus a small UI to inspect log occurrences over time. This wasn’t built because I think the world needed “another logging system” or because I’m an expert here. I mostly wanted to learn by building something end to end: ingestion paths, storage layering, querying, retention, auth/TLS, and some UI work. Repo: https://github.com/Saumya40-codes/LogsGO I’d genuinely appreciate feedback, including “this design is wrong for X reason” type feedback. If parts of it feel overengineered / naive / badly thought through, that’s useful for me too. Comments URL: https://news.ycombinator.com/item?id=47815402 Points: 1 # Comments: 0