GLM Excel Custom 现在是当成主力插件在用了,接入公司提供的API后很爽,不再受到数据脱敏问题和API限制问题了 3 个帖子 - 2 位参与者 阅读完整话题
如题,我的编辑器是idea,用的qoder cn插件,它可以自定义模型接入 deepseek,但是有上下文限制,最多 200k,用光了就得新开会话。一个问题,它分析完了,比如列出4步,第一步还没执行完,就耗光了。公司电脑,性能一般,codex装不了。我用idea也用习惯了。 5 个帖子 - 5 位参与者 阅读完整话题
我有多个节点,包括机场订阅的和自己个人部署的,机场的就添加了订阅每天更新,个人的就在节点 & 策略组管理那里添加了代理集配置 Proxy-provider 。但是每次订阅更新后,代理集的配置的节点就被删除了,请教下各位如何解决?
我有多个节点,包括机场订阅的和自己个人部署的,机场的就添加了订阅每天更新,个人的就在节点 & 策略组管理那里添加了代理集配置 Proxy-provider 。但是每次订阅更新后,代理集的配置的节点就被删除了,请教下各位如何解决?
公司买的pro,用的公司自定义邮箱, 被封的差不多了。 然后他们还申诉不回。 请教还有什么低价地区买gpt20pro可以通过applepay + 招商全币种卡订阅的, 小组打算拼车用。 加拿大可以吗? 菲律宾就算了,要绕很多。 2 个帖子 - 2 位参与者 阅读完整话题
之前用v3.15.0,codex自定义的供应商可以获取模型,知道这家目前还存活的模型有哪些,选择后还可以测试这个模型是否连通成功。 现在更新到v3.16.2之后,没法获取模型了,这咋知道这家供应商还有哪些模型活着? 比如用公益站,一天死好多次模型,一直在变,想要用就得一直关注他还有哪些模型能用,现在更新后看不了了,该怎么办呢? 1 个帖子 - 1 位参与者 阅读完整话题
绑了一个免费的二级域名到cf账号里,现在在worker的配置页能看到domain标签,但是配置了以后访问自定义域名没生效,有没有佬可以截图让我抄一下配置看看我哪弄错了 9 个帖子 - 6 位参与者 阅读完整话题
之前一直是在快捷键设置的「 APP 快捷键」这里自定义了一个,升级到 macOS 27 后发现不起作用了 大家有遇到吗?
之前一直是在快捷键设置的「 APP 快捷键」这里自定义了一个,升级到 macOS 27 后发现不起作用了 大家有遇到吗?
之前一直是在快捷键设置的「 APP 快捷键」这里自定义了一个,升级到 macOS 27 后发现不起作用了 大家有遇到吗?
综合L站、公众号各位佬友 大v的评测,fable是真的很强,生产重构中大型项目完全不在话下了,所以AI再发展几年,程序员的角色会不会只是一个传话筒了,领导说改哪传给ai,客户哪里不满意,传给ai。。 10 个帖子 - 10 位参与者 阅读完整话题
想不起来如何设置的了,右上角的“添加自定义翻译服务”好像出来的都是“自定义ai”,好像应该还有个“自定义api”在哪里打开来着?找了三遍没找到 5 个帖子 - 4 位参与者 阅读完整话题
证件照换底(支持自定义颜色代码) <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>证件照换底 - 离线隐私处理</title> <style> *{margin:0;padding:0;box-sizing:border-box;} body{ font-family: -apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif; background:#f0f2f5;min-height:100vh;color:#1a1a2e;line-height:1.6; } .header{ background:#fff;border-bottom:1px solid #e0e0e0;padding:14px 0; position:sticky;top:0;z-index:100;box-shadow:0 1px 3px rgba(0,0,0,0.06); } .header-inner{max-width:1200px;margin:0 auto;padding:0 24px;display:flex;align-items:center;justify-content:space-between;} .logo{display:flex;align-items:center;gap:10px;font-size:1.25rem;font-weight:700;} .logo-icon{width:38px;height:38px;border-radius:11px;background:linear-gradient(135deg,#4f6ef7,#7b8ff7);display:flex;align-items:center;justify-content:center;font-size:1.3rem;color:#fff;} .privacy-badge{display:flex;align-items:center;gap:6px;font-size:0.8rem;color:#27ae60;background:#eafaf1;padding:5px 12px;border-radius:20px;font-weight:600;} .privacy-dot{width:7px;height:7px;border-radius:50%;background:#27ae60;animation:pulse 2s infinite;} @keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}} .main-container{max-width:1200px;margin:0 auto;padding:24px;display:grid;grid-template-columns:1fr 1fr;gap:24px;} @media(max-width:900px){.main-container{grid-template-columns:1fr;max-width:520px;}} .card{background:#fff;border-radius:16px;padding:22px;box-shadow:0 4px 24px rgba(0,0,0,0.08);border:1px solid #e0e0e0;} .card-title{font-size:1rem;font-weight:700;margin-bottom:16px;} .upload-zone{border:2px dashed #d0d5e0;border-radius:10px;padding:36px 20px;text-align:center;cursor:pointer;background:#fafbfc;position:relative;min-height:180px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;transition:0.25s;} .upload-zone:hover,.upload-zone.drag-over{border-color:#4f6ef7;background:#eef1ff;} .upload-zone.has-image{border-style:solid;border-color:#e0e0e0;padding:6px;min-height:auto;} .upload-icon{font-size:2.8rem;opacity:0.5;} .upload-text{font-size:0.9rem;color:#555;} .upload-hint{font-size:0.75rem;color:#999;} .upload-zone input[type="file"]{position:absolute;inset:0;opacity:0;cursor:pointer;} .preview-image{width:100%;max-height:350px;object-fit:contain;display:block;border-radius:5px;} .image-container{position:relative;display:block;width:100%;} .image-actions-overlay{position:absolute;top:8px;right:8px;z-index:5;} .btn-icon-sm{width:32px;height:32px;border-radius:50%;border:none;background:rgba(0,0,0,0.5);color:#fff;cursor:pointer;font-size:0.85rem;display:flex;align-items:center;justify-content:center;} .btn{display:inline-flex;align-items:center;justify-content:center;gap:5px;padding:9px 18px;border-radius:25px;font-size:0.88rem;font-weight:600;cursor:pointer;border:none;transition:0.25s;white-space:nowrap;} .btn:active{transform:scale(0.96);} .btn-primary{background:#4f6ef7;color:#fff;} .btn-primary:hover{background:#3b54db;} .btn-outline{background:#fff;color:#4f6ef7;border:2px solid #4f6ef7;} .btn-outline:hover{background:#eef1ff;} .btn-success{background:#27ae60;color:#fff;} .btn-success:hover{opacity:0.9;} .btn-lg{padding:11px 26px;font-size:0.95rem;border-radius:28px;} .btn:disabled{opacity:0.5;cursor:not-allowed;pointer-events:none;} .btn-block{width:100%;} .section-label{font-size:0.82rem;font-weight:600;margin:14px 0 8px;} .color-presets{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:8px;} .color-preset{width:40px;height:40px;border-radius:50%;cursor:pointer;border:3px solid transparent;transition:0.25s;box-shadow:0 1px 3px rgba(0,0,0,0.1);position:relative;} .color-preset:hover{transform:scale(1.12);} .color-preset.active{border-color:#1a1a2e;box-shadow:0 0 0 4px rgba(0,0,0,0.08);} .color-preset.active::after{content:'✓';position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:bold;font-size:0.9rem;text-shadow:0 1px 2px rgba(0,0,0,0.5);} .custom-color-section{background:#f8f9fb;border-radius:12px;padding:12px 14px;margin-bottom:4px;border:1px solid #e8eaef;} .custom-color-row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;} .custom-color-row input[type="color"]{width:40px;height:40px;border-radius:50%;border:2px solid #e0e0e0;cursor:pointer;padding:2px;flex-shrink:0;} .custom-color-row input[type="color"]::-webkit-color-swatch-wrapper{padding:0;} .custom-color-row input[type="color"]::-webkit-color-swatch{border-radius:50%;border:none;} .hex-input-wrapper{position:relative;flex:1;min-width:110px;} .hex-input-wrapper .hash{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:#999;font-weight:600;font-size:0.85rem;pointer-events:none;} .hex-input{width:100%;padding:8px 10px 8px 24px;border:2px solid #e0e5ec;border-radius:10px;font-size:0.85rem;font-family:monospace;font-weight:600;color:#1a1a2e;background:#fff;outline:none;transition:0.25s;} .hex-input:focus{border-color:#4f6ef7;box-shadow:0 0 0 3px rgba(79,110,247,0.1);} .hex-input.valid{border-color:#27ae60;background:#f8fdf8;} .hex-input.invalid{border-color:#e74c3c;background:#fef8f8;} .color-preview-dot{width:30px;height:30px;border-radius:50%;border:2px solid #ddd;flex-shrink:0;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);} .control-group{margin-bottom:12px;} .control-label{display:flex;justify-content:space-between;align-items:center;font-size:0.82rem;font-weight:600;margin-bottom:5px;} .control-value{font-size:0.75rem;color:#555;background:#f5f6f8;padding:2px 10px;border-radius:12px;} input[type="range"]{-webkit-appearance:none;width:100%;height:5px;border-radius:3px;background:#e0e5ec;outline:none;cursor:pointer;} input[type="range"]::-webkit-slider-thumb{-webkit-appearance:none;width:20px;height:20px;border-radius:50%;background:#4f6ef7;cursor:pointer;} .size-presets{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:8px;} .size-preset{padding:10px 12px;border-radius:8px;border:2px solid #e0e0e0;cursor:pointer;text-align:center;font-size:0.82rem;font-weight:600;transition:0.25s;background:#fafbfc;} .size-preset:hover{border-color:#4f6ef7;background:#eef1ff;} .size-preset.active{border-color:#4f6ef7;background:#eef1ff;color:#4f6ef7;} .size-preset .size-dims{font-size:0.7rem;color:#555;font-weight:400;} .result-container{text-align:center;} .result-image{max-width:100%;max-height:350px;border-radius:10px;box-shadow:0 4px 24px rgba(0,0,0,0.08);border:1px solid #e0e0e0;} .result-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:220px;color:#bbb;gap:10px;} .result-placeholder .icon{font-size:3.5rem;} .action-row{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px;} .toast{position:fixed;bottom:30px;left:50%;transform:translateX(-50%) translateY(100px);background:#1a1a2e;color:#fff;padding:10px 22px;border-radius:25px;font-size:0.85rem;font-weight:600;z-index:999;opacity:0;transition:all 0.35s;pointer-events:none;box-shadow:0 8px 30px rgba(0,0,0,0.25);} .toast.show{opacity:1;transform:translateX(-50%) translateY(0);} .toast.success{background:#27ae60;} .toast.error{background:#e74c3c;} .spinner{display:inline-block;width:16px;height:16px;border:2px solid rgba(255,255,255,0.3);border-top-color:#fff;border-radius:50%;animation:spin 0.7s linear infinite;} @keyframes spin{to{transform:rotate(360deg);}} .footer{text-align:center;padding:18px;font-size:0.75rem;color:#aaa;} </style> </head> <body> <header class="header"> <div class="header-inner"> <div class="logo"><div class="logo-icon">📸</div><span>证件照换底</span></div> <div class="privacy-badge"><span class="privacy-dot"></span>离线处理 · 隐私安全</div> </div> </header> <div class="main-container"> <!-- ========== 左侧面板 ========== --> <div class="card"> <div class="card-title">⚙️ 上传与设置</div> <div class="upload-zone" id="uploadZone"> <span class="upload-icon">📷</span> <span class="upload-text">点击或拖拽上传证件照</span> <span class="upload-hint">支持 JPG / PNG / WebP</span> <input type="file" id="fileInput" accept="image/*"> </div> <div class="section-label">🎨 背景颜色(实时预览)</div> <div class="color-presets" id="colorPresets"> <div class="color-preset active" data-color="#FF0000" style="background:#FF0000;" title="红色"></div> <div class="color-preset" data-color="#FFFFFF" style="background:#FFFFFF;" title="白色"></div> <div class="color-preset" data-color="#438EDB" style="background:#438EDB;" title="蓝色"></div> <div class="color-preset" data-color="#1AAD19" style="background:#1AAD19;" title="绿色"></div> <div class="color-preset" data-color="#808080" style="background:#808080;" title="灰色"></div> <div class="color-preset" data-color="#003399" style="background:#003399;" title="深蓝"></div> </div> <div class="custom-color-section"> <div style="font-size:0.78rem;font-weight:600;margin-bottom:8px;color:#555;">🎯 自定义颜色</div> <div class="custom-color-row"> <input type="color" id="customColorPicker" value="#FF0000" title="取色器"> <div class="hex-input-wrapper"> <span class="hash">#</span> <input type="text" class="hex-input valid" id="hexInput" value="FF0000" maxlength="6" placeholder="输入6位HEX色值"> </div> <div class="color-preview-dot" id="colorPreviewDot" style="background:#FF0000;" title="当前颜色"></div> </div> </div> <div class="control-group" style="margin-top:14px;"> <div class="control-label"><span>🔍 容差范围</span><span class="control-value" id="toleranceVal">50</span></div> <input type="range" id="toleranceSlider" min="10" max="120" value="50" step="1"> </div> <div class="control-group"> <div class="control-label"><span>✨ 边缘羽化</span><span class="control-value" id="featherVal">1</span></div> <input type="range" id="featherSlider" min="0" max="5" value="1" step="0.5"> </div> <div class="control-group"> <div class="control-label"><span>☀️ 亮度</span><span class="control-value" id="brightnessVal">0</span></div> <input type="range" id="brightnessSlider" min="-30" max="30" value="0" step="1"> </div> <div class="section-label">📐 裁剪尺寸</div> <div class="size-presets" id="sizePresets"> <div class="size-preset active" data-width="295" data-height="413" data-name="一寸"><div>一寸</div><div class="size-dims">25×35mm</div></div> <div class="size-preset" data-width="413" data-height="579" data-name="二寸"><div>二寸</div><div class="size-dims">35×49mm</div></div> <div class="size-preset" data-width="260" data-height="378" data-name="小一寸"><div>小一寸</div><div class="size-dims">22×32mm</div></div> <div class="size-preset" data-width="390" data-height="567" data-name="小二寸"><div>小二寸</div><div class="size-dims">33×48mm</div></div> </div> <div class="action-row"> <button class="btn btn-primary btn-lg" id="processBtn" disabled>🎯 开始换底</button> <button class="btn btn-outline" id="resetBtn">🔄 重置</button> </div> </div> <!-- ========== 右侧面板 ========== --> <div class="card"> <div class="card-title">🖼️ 结果预览</div> <div class="result-container"> <div class="result-placeholder" id="resultPlaceholder"> <span class="icon">🖼️</span> <span>处理后的照片将显示在这里</span> </div> <img class="result-image" id="resultImage" style="display:none;" alt="处理结果"> </div> <div class="action-row"> <button class="btn btn-success btn-block" id="downloadBtn" disabled>💾 下载照片</button> </div> </div> </div> <div class="toast" id="toast"></div> <div class="footer">🔒 所有图片处理均在浏览器本地完成,不会上传到任何服务器</div> <script> (function() { // ==================== DOM引用 ==================== var uploadZone = document.getElementById('uploadZone'); var fileInput = document.getElementById('fileInput'); var processBtn = document.getElementById('processBtn'); var resetBtn = document.getElementById('resetBtn'); var downloadBtn = document.getElementById('downloadBtn'); var resultImage = document.getElementById('resultImage'); var resultPH = document.getElementById('resultPlaceholder'); var toastEl = document.getElementById('toast'); var colorPicker = document.getElementById('customColorPicker'); var hexInput = document.getElementById('hexInput'); var colorDot = document.getElementById('colorPreviewDot'); var tolSlider = document.getElementById('toleranceSlider'); var tolVal = document.getElementById('toleranceVal'); var feaSlider = document.getElementById('featherSlider'); var feaVal = document.getElementById('featherVal'); var briSlider = document.getElementById('brightnessSlider'); var briVal = document.getElementById('brightnessVal'); // ==================== 状态 ==================== var originalImage = null; var resultDataURL = null; var currentColor = '#FF0000'; var currentSize = { w: 295, h: 413, name: '一寸' }; var toastTimer = null; var autoTimer = null; var cachedMask = null; // ==================== Toast ==================== function toast(msg, type) { if (toastTimer) clearTimeout(toastTimer); toastEl.textContent = msg; toastEl.className = 'toast ' + (type || '') + ' show'; toastTimer = setTimeout(function() { toastEl.classList.remove('show'); }, 2000); } // ==================== 颜色工具 ==================== function hex3to6(s) { s = s.toUpperCase(); if (s.length === 3) { return s[0]+s[0]+s[1]+s[1]+s[2]+s[2]; } return s; } function isValidHex(s) { return /^[0-9a-fA-F]{3}$/.test(s) || /^[0-9a-fA-F]{6}$/.test(s); } function hexToRgb(hex) { hex = hex.replace('#', ''); if (hex.length === 3) hex = hex3to6(hex); return { r: parseInt(hex.substr(0,2), 16), g: parseInt(hex.substr(2,2), 16), b: parseInt(hex.substr(4,2), 16) }; } // 同步所有颜色UI function applyColor(hex6, source) { hex6 = hex6.toUpperCase(); if (!/^[0-9a-fA-F]{6}$/.test(hex6)) return; currentColor = '#' + hex6; // 取色器 if (source !== 'picker') { colorPicker.value = currentColor; } // 输入框 if (source !== 'input') { hexInput.value = hex6; hexInput.className = 'hex-input valid'; } // 预设圆点 if (source !== 'preset') { var all = document.querySelectorAll('.color-preset'); for (var i = 0; i < all.length; i++) { all[i].classList.remove('active'); } var m = document.querySelector('.color-preset[data-color="#' + hex6 + '"]'); if (m) m.classList.add('active'); } // 预览圆点 colorDot.style.background = currentColor; // 实时更新结果 scheduleAuto(); } // ==================== 颜色事件 ==================== document.getElementById('colorPresets').addEventListener('click', function(e) { var p = e.target.closest('.color-preset'); if (!p) return; var hex6 = p.getAttribute('data-color').replace('#', ''); var all = document.querySelectorAll('.color-preset'); for (var i = 0; i < all.length; i++) all[i].classList.remove('active'); p.classList.add('active'); applyColor(hex6, 'preset'); }); colorPicker.addEventListener('input', function() { var hex = colorPicker.value.replace('#', ''); if (isValidHex(hex)) { applyColor(hex3to6(hex), 'picker'); } }); hexInput.addEventListener('input', function() { var raw = hexInput.value.replace(/[^0-9a-fA-F]/g, '').slice(0, 6); hexInput.value = raw; if (raw.length === 6 && /^[0-9a-fA-F]{6}$/.test(raw)) { hexInput.className = 'hex-input valid'; applyColor(raw, 'input'); } else if (raw.length === 3 && /^[0-9a-fA-F]{3}$/.test(raw)) { hexInput.className = 'hex-input valid'; applyColor(hex3to6(raw), 'input'); } else if (raw.length === 0) { hexInput.className = 'hex-input'; } else if (/^[0-9a-fA-F]+$/.test(raw)) { hexInput.className = 'hex-input'; } else { hexInput.className = 'hex-input invalid'; } }); hexInput.addEventListener('blur', function() { var raw = hexInput.value; if (isValidHex(raw)) { var h6 = hex3to6(raw); hexInput.value = h6; hexInput.className = 'hex-input valid'; applyColor(h6, 'input'); } }); hexInput.addEventListener('paste', function(e) { e.preventDefault(); var t = (e.clipboardData || window.clipboardData).getData('text'); t = t.replace('#', '').replace(/[^0-9a-fA-F]/g, '').slice(0, 6); hexInput.value = t; if (isValidHex(t)) { hexInput.className = 'hex-input valid'; applyColor(hex3to6(t), 'input'); } }); // ==================== 尺寸 ==================== document.getElementById('sizePresets').addEventListener('click', function(e) { var p = e.target.closest('.size-preset'); if (!p) return; var all = document.querySelectorAll('.size-preset'); for (var i = 0; i < all.length; i++) all[i].classList.remove('active'); p.classList.add('active'); currentSize = { w: parseInt(p.getAttribute('data-width')), h: parseInt(p.getAttribute('data-height')), name: p.getAttribute('data-name') }; scheduleAuto(); }); // ==================== 滑块 ==================== tolSlider.addEventListener('input', function() { tolVal.textContent = tolSlider.value; cachedMask = null; scheduleAuto(); }); feaSlider.addEventListener('input', function() { feaVal.textContent = feaSlider.value; scheduleAuto(); }); briSlider.addEventListener('input', function() { briVal.textContent = briSlider.value; scheduleAuto(); }); // ==================== 防抖 ==================== function scheduleAuto() { if (!originalImage) return; if (autoTimer) clearTimeout(autoTimer); autoTimer = setTimeout(function() { doProcess(true); }, 200); } // ==================== 上传 ==================== uploadZone.addEventListener('click', function(e) { if (e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT') return; fileInput.click(); }); fileInput.addEventListener('change', function(e) { if (e.target.files && e.target.files[0]) { loadFile(e.target.files[0]); } }); uploadZone.addEventListener('dragover', function(e) { e.preventDefault(); uploadZone.classList.add('drag-over'); }); uploadZone.addEventListener('dragleave', function() { uploadZone.classList.remove('drag-over'); }); uploadZone.addEventListener('drop', function(e) { e.preventDefault(); uploadZone.classList.remove('drag-over'); if (e.dataTransfer.files && e.dataTransfer.files[0]) { loadFile(e.dataTransfer.files[0]); } }); function loadFile(file) { if (!file.type.match(/image\//)) { toast('请上传图片文件', 'error'); return; } var reader = new FileReader(); reader.onload = function(ev) { var img = new Image(); img.onload = function() { originalImage = img; cachedMask = null; showPreview(img); processBtn.disabled = false; toast('图片加载成功', 'success'); doProcess(false); }; img.onerror = function() { toast('图片加载失败', 'error'); }; img.src = ev.target.result; }; reader.readAsDataURL(file); } function showPreview(img) { // 清除旧预览 var old = uploadZone.querySelector('.image-container'); if (old) old.remove(); var iconEl = uploadZone.querySelector('.upload-icon'); var textEl = uploadZone.querySelector('.upload-text'); var hintEl = uploadZone.querySelector('.upload-hint'); uploadZone.classList.add('has-image'); if (iconEl) iconEl.style.display = 'none'; if (textEl) textEl.style.display = 'none'; if (hintEl) hintEl.style.display = 'none'; var container = document.createElement('div'); container.className = 'image-container'; var pImg = document.createElement('img'); pImg.className = 'preview-image'; pImg.src = img.src; pImg.alt = '原始照片'; var overlay = document.createElement('div'); overlay.className = 'image-actions-overlay'; var delBtn = document.createElement('button'); delBtn.className = 'btn-icon-sm'; delBtn.innerHTML = '✕'; delBtn.title = '移除图片'; delBtn.addEventListener('click', function(ev) { ev.stopPropagation(); resetAll(); }); overlay.appendChild(delBtn); container.appendChild(pImg); container.appendChild(overlay); uploadZone.appendChild(container); } // ==================== 核心算法 ==================== function doProcess(silent) { if (!originalImage) return; var tolerance = parseInt(tolSlider.value); var featherR = parseFloat(feaSlider.value); var brightness = parseInt(briSlider.value); var target = hexToRgb(currentColor); var srcW = originalImage.naturalWidth; var srcH = originalImage.naturalHeight; // 画到canvas var canvas = document.createElement('canvas'); canvas.width = srcW; canvas.height = srcH; var ctx = canvas.getContext('2d'); ctx.drawImage(originalImage, 0, 0); var imgData = ctx.getImageData(0, 0, srcW, srcH); var data = imgData.data; var w = srcW; var h = srcH; // 步骤1:建立遮罩(仅首次或容差改变时) if (cachedMask === null) { // 采样边缘 var samples = []; var step = Math.max(1, Math.floor(Math.min(w, h) / 30)); var x, y; for (x = 0; x < w; x += step) { samples.push(getPixel(data, w, x, 0)); samples.push(getPixel(data, w, x, h - 1)); } for (y = 0; y < h; y += step) { samples.push(getPixel(data, w, 0, y)); samples.push(getPixel(data, w, w - 1, y)); } // 中位数 samples.sort(function(a, b) { return (a.r + a.g + a.b) - (b.r + b.g + b.b); }); var mid = samples[Math.floor(samples.length / 2)]; var bgR = mid.r; var bgG = mid.g; var bgB = mid.b; // 颜色距离 var mask = new Uint8Array(w * h); var tolSq = tolerance * tolerance; var i; for (i = 0; i < w * h; i++) { var pi = i * 4; var dr = data[pi] - bgR; var dg = data[pi + 1] - bgG; var db = data[pi + 2] - bgB; if (dr * dr + dg * dg + db * db <= tolSq) { mask[i] = 1; } } // Flood fill 从边缘连通 var visited = new Uint8Array(w * h); var queue = []; var head = 0; var x2, y2; for (x2 = 0; x2 < w; x2++) { if (mask[x2] === 1 && !visited[x2]) { visited[x2] = 1; queue.push(x2); } var bIdx = (h - 1) * w + x2; if (mask[bIdx] === 1 && !visited[bIdx]) { visited[bIdx] = 1; queue.push(bIdx); } } for (y2 = 0; y2 < h; y2++) { var lIdx = y2 * w; if (mask[lIdx] === 1 && !visited[lIdx]) { visited[lIdx] = 1; queue.push(lIdx); } var rIdx = y2 * w + (w - 1); if (mask[rIdx] === 1 && !visited[rIdx]) { visited[rIdx] = 1; queue.push(rIdx); } } while (head < queue.length) { var qIdx = queue[head++]; var qx = qIdx % w; var qy = Math.floor(qIdx / w); var nb = []; if (qx > 0) nb.push(qIdx - 1); if (qx < w - 1) nb.push(qIdx + 1); if (qy > 0) nb.push(qIdx - w); if (qy < h - 1) nb.push(qIdx + w); for (var ni = 0; ni < nb.length; ni++) { var nIdx = nb[ni]; if (mask[nIdx] === 1 && !visited[nIdx]) { visited[nIdx] = 1; queue.push(nIdx); } } } cachedMask = visited; } // 步骤2:从原始图重新着色 var origCanvas = document.createElement('canvas'); origCanvas.width = w; origCanvas.height = h; var origCtx = origCanvas.getContext('2d'); origCtx.drawImage(originalImage, 0, 0); var origData = origCtx.getImageData(0, 0, w, h).data; var work = new Uint8ClampedArray(origData.length); var k; for (k = 0; k < origData.length; k++) { work[k] = origData[k]; } var featherPx = Math.round(featherR); var j; for (j = 0; j < work.length; j += 4) { var idx = j / 4; if (cachedMask[idx] === 1) { var alpha = 1; if (featherPx > 0) { alpha = getFeather(cachedMask, idx, w, h, featherPx); } work[j] = Math.round(work[j] * (1 - alpha) + target.r * alpha); work[j + 1] = Math.round(work[j + 1] * (1 - alpha) + target.g * alpha); work[j + 2] = Math.round(work[j + 2] * (1 - alpha) + target.b * alpha); } } // 亮度 if (brightness !== 0) { var bj; for (bj = 0; bj < work.length; bj += 4) { work[bj] = clamp(work[bj] + brightness, 0, 255); work[bj + 1] = clamp(work[bj + 1] + brightness, 0, 255); work[bj + 2] = clamp(work[bj + 2] + brightness, 0, 255); } } var resultID = new ImageData(work, w, h); var workCanvas = document.createElement('canvas'); workCanvas.width = w; workCanvas.height = h; var workCtx = workCanvas.getContext('2d'); workCtx.putImageData(resultID, 0, 0); // 裁剪 var finalCanvas = cropCanvas(workCanvas, currentSize.w, currentSize.h); resultDataURL = finalCanvas.toDataURL('image/png'); resultImage.src = resultDataURL; resultImage.style.display = 'block'; resultPH.style.display = 'none'; downloadBtn.disabled = false; if (!silent) { toast('换底完成!', 'success'); } } function getPixel(data, w, x, y) { var i = (y * w + x) * 4; return { r: data[i], g: data[i + 1], b: data[i + 2] }; } function getFeather(mask, idx, w, h, radius) { var y = Math.floor(idx / w); var x = idx % w; var fg = 0; var total = 0; var dy, dx; for (dy = -radius; dy <= radius; dy++) { for (dx = -radius; dx <= radius; dx++) { var nx = x + dx; var ny = y + dy; if (nx >= 0 && nx < w && ny >= 0 && ny < h) { total++; if (mask[ny * w + nx] === 0) fg++; } } } if (total === 0) return 1; var ratio = fg / total; if (ratio > 0.6) return 0; if (ratio > 0.3) return 0.5; return 1; } function cropCanvas(srcCanvas, tw, th) { var sw = srcCanvas.width; var sh = srcCanvas.height; var sa = sw / sh; var ta = tw / th; var cw, ch, ox, oy; if (sa > ta) { ch = sh; cw = Math.round(sh * ta); ox = Math.round((sw - cw) / 2); oy = 0; } else { cw = sw; ch = Math.round(sw / ta); ox = 0; oy = Math.round((sh - ch) / 2); } var fc = document.createElement('canvas'); fc.width = tw; fc.height = th; var fctx = fc.getContext('2d'); fctx.imageSmoothingEnabled = true; fctx.imageSmoothingQuality = 'high'; fctx.drawImage(srcCanvas, ox, oy, cw, ch, 0, 0, tw, th); return fc; } function clamp(v, min, max) { return Math.max(min, Math.min(max, v)); } // ==================== 按钮 ==================== processBtn.addEventListener('click', function() { processBtn.disabled = true; processBtn.innerHTML = '<span class="spinner"></span> 处理中...'; cachedMask = null; setTimeout(function() { try { doProcess(false); } catch(err) { console.error(err); toast('处理出错: ' + err.message, 'error'); } processBtn.disabled = false; processBtn.innerHTML = '🎯 开始换底'; }, 30); }); resetBtn.addEventListener('click', resetAll); downloadBtn.addEventListener('click', function() { if (!resultDataURL) return; var a = document.createElement('a'); a.download = '证件照_' + currentSize.name + '_' + Date.now() + '.png'; a.href = resultDataURL; document.body.appendChild(a); a.click(); document.body.removeChild(a); toast('下载中', 'success'); }); function resetAll() { originalImage = null; resultDataURL = null; cachedMask = null; if (autoTimer) clearTimeout(autoTimer); resultImage.style.display = 'none'; resultImage.src = ''; resultPH.style.display = ''; downloadBtn.disabled = true; processBtn.disabled = true; processBtn.innerHTML = '🎯 开始换底'; uploadZone.classList.remove('has-image'); var old = uploadZone.querySelector('.image-container'); if (old) old.remove(); var iconEl = uploadZone.querySelector('.upload-icon'); var textEl = uploadZone.querySelector('.upload-text'); var hintEl = uploadZone.querySelector('.upload-hint'); if (iconEl) iconEl.style.display = ''; if (textEl) textEl.style.display = ''; if (hintEl) hintEl.style.display = ''; fileInput.value = ''; tolSlider.value = 50; tolVal.textContent = '50'; feaSlider.value = 1; feaVal.textContent = '1'; briSlider.value = 0; briVal.textContent = '0'; currentColor = '#FF0000'; colorPicker.value = '#FF0000'; hexInput.value = 'FF0000'; hexInput.className = 'hex-input valid'; colorDot.style.background = '#FF0000'; var all = document.querySelectorAll('.color-preset'); for (var i = 0; i < all.length; i++) all[i].classList.remove('active'); var red = document.querySelector('.color-preset[data-color="#FF0000"]'); if (red) red.classList.add('active'); var allS = document.querySelectorAll('.size-preset'); for (var j = 0; j < allS.length; j++) allS[j].classList.remove('active'); var yc = document.querySelector('.size-preset[data-name="一寸"]'); if (yc) yc.classList.add('active'); currentSize = { w: 295, h: 413, name: '一寸' }; } // ==================== 快捷键 ==================== document.addEventListener('keydown', function(e) { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); if (!processBtn.disabled) processBtn.click(); } if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); if (!downloadBtn.disabled) downloadBtn.click(); } }); console.log('证件照换底工具已就绪 - 离线处理 - 实时预览'); })(); </script> </body> </html> 2 个帖子 - 1 位参与者 阅读完整话题
我有一个长期 vc 的项目,我在 AGENTS.md里定义了如果多次遇到相同问题要记录进复盘日志,并在开始任务时阅读进行规避.两个月过去了,我们一起看看AI都记录了什么. 建议大家也开一个类似的复盘日志,用来证据指导人类进行 Harness 迭代 复盘日志模版为了方便佬友复制就不当LLM产出截图了 工程复盘日志 (RETRO.md) 用途 : 记录开发过程中反复出现的错误、vibe coding 中遇到的典型困难、以及解决方案。 当类似问题再次出现时,先来这里查询,避免重复踩坑。 维护规则 : 每条记录必须包含:现象、根因、是否已解决、解决方案(或规避方式)。 未解决的问题必须标注 [未解决] ,已解决的标注 [已解决] 。 新条目追加在对应分类末尾,不要打乱已有顺序。 如果某条经验已经固化为 AGENTS.md 的硬约束或棘轮条目,在此标注"已升级为规则"并注明位置。 新增条目请复制以下模板: ### RETRO-XXX: 简短标题 [已解决/未解决] - **现象**: 具体的错误表现或困难描述。 - **根因**: 为什么会出现这个问题。 - **解决方案**: 怎么修复的,或者当前的规避方式。 - **后续防护**: 是否已升级为 AGENTS.md 规则,如果是注明具体条目。 2 个帖子 - 2 位参与者 阅读完整话题
头部厂商都意图黑箱化算力真实价值,变成它们自己随意定义的经济价值,最终它们都想掌握AI时代的铸币权 10 个帖子 - 5 位参与者 阅读完整话题
我平时用codex app的GPT PRO套餐,但是不够用啊,codex可比cursor好用太多了,最近迷上codex了,然后我自己也有买的token,但是我不知道如何让codex使用自定义的。各位佬能帮小的指路一下呗,谢谢各位佬 9 个帖子 - 8 位参与者 阅读完整话题
腾讯也推出了 AgentMail ,但是目前不支持自定义邮箱前缀,且只支持创建两个邮箱,感觉有点鸡肋 2 个帖子 - 2 位参与者 阅读完整话题
这么“新”的功能,竟然是在 watchOS27 里面介绍的,iOS27 里面提都没提 但是背景只能从给定的预设里面选,不能自己上传图片😂 也只支持二维码和条形码 总的来说,很难评😂
这么“新”的功能,竟然是在 watchOS27 里面介绍的,iOS27 里面提都没提 但是背景只能从给定的预设里面选,不能自己上传图片😂 也只支持二维码和条形码 总的来说,很难评😂
这么“新”的功能,竟然是在 watchOS27 里面介绍的,iOS27 里面提都没提 但是背景只能从给定的预设里面选,不能自己上传图片😂 也只支持二维码和条形码 总的来说,很难评😂