今日已更新 257 条资讯 | 累计 40702 条内容
关于我们

标签:#HP

找到 185 篇相关文章

AI 资讯

Supercharge Your Algorithmic Trading with CoinQuant PHP: The Ultimate SDK for Laravel and PHP 8.1+

Are you tired of wrestling with raw cURL requests, parsing Server-Sent Events (SSE) by hand, and building complex polling loops just to interact with trading APIs? If you are a PHP developer or a Laravel enthusiast looking to dive into algorithmic trading, your life is about to get a whole lot easier. Meet coinquant-php , the official PHP and Laravel SDK for the CoinQuant Public API. CoinQuant is revolutionizing the way we approach algorithmic trading. It takes a trading idea described in plain English and turns it into a backtestable strategy using advanced AI. However, integrating such powerful tools into your own applications can often be a daunting task. That is where coinquant-php steps in. This robust SDK wraps the public API, providing a seamless, developer-friendly experience that lets you focus on what really matters: building profitable trading strategies. In this comprehensive guide, we will explore why coinquant-php is a game-changer for PHP developers, delve into its standout features, and show you how to get started in minutes. Why Choose CoinQuant PHP? The landscape of algorithmic trading is often dominated by Python, but PHP remains a powerhouse for web development, especially with frameworks like Laravel. coinquant-php bridges the gap, allowing PHP developers to leverage the cutting-edge AI capabilities of CoinQuant without leaving their preferred ecosystem. 1. Built for Modern PHP The library is designed for the modern era of PHP. It requires PHP 8.1+ and embraces strict typing, ensuring your code is robust and free from legacy baggage. Whether you are building a standalone script or a complex enterprise application, the SDK provides a solid foundation. 2. First-Class Laravel Integration If you are using Laravel 10, 11, 12, or 13, you are in for a treat. The package auto-registers its ServiceProvider and Facade , meaning there is absolutely zero manual wiring required. You can simply install the package and start using the CoinQuant facade anywhere

2026-07-22 原文 →
AI 资讯

How to Generate Verifiable PDF Certificates in Laravel

When a learner finishes a course they want a certificate that behaves like a document: something they can print at full quality, attach to a job application and that an employer can check is genuine. In this tutorial you'll build exactly that in Laravel: a Blade-designed certificate rendered as a vector PDF, with a QR code that resolves to a verification page and an email that delivers it automatically. This post originally appeared on Accreditly as How to generate verifiable PDF certificates in Laravel . Keep the design in Blade The certificate is an ordinary Blade view. Everything lives in one file, styles inline, so the markup you preview in the browser is exactly what gets rendered. If you want a designed starting point rather than a blank page, the certificate of completion template is a good base to adapt. {{-- resources/views/certificates/template.blade.php --}} <!doctype html> <html> <head> <meta charset="utf-8"> <style> body { font-family: Georgia, serif; color: #1a2233; margin: 0; } .certificate { padding: 60px; border: 6px double #b28a2f; margin: 24px; text-align: center; } .heading { font-size: 15px; letter-spacing: 4px; text-transform: uppercase; color: #b28a2f; } h1 { font-size: 44px; margin: 24px 0 8px; } .course { font-size: 22px; margin: 4px 0 28px; } .issued { font-size: 15px; color: #5a6478; } .qr { margin-top: 36px; } .verify-url { font-size: 12px; color: #5a6478; } </style> </head> <body> <div class="certificate"> <p class="heading">Certificate of Completion</p> <h1>{{ $certificate->user->name }}</h1> <p class="course">has completed {{ $certificate->course }}</p> <p class="issued">Issued {{ $certificate->issued_at->format('j F Y') }}</p> <div class="qr"> {!! QrCode::size(110)->generate(route('certificates.verify', $certificate)) !!} </div> <p class="verify-url">{{ route('certificates.verify', $certificate) }}</p> </div> </body> </html> Two things are doing quiet work here. The design flows like a document rather than being pinned to fixed pixel

2026-07-22 原文 →
AI 资讯

🐘 TypeScript for PHP? YES, that's possible! And it's not what you think...

It is time to bring opposites together, to stir up these stagnant waters, and to restore an industry that is dynamic from within, with multiple connections that were previously impossible or unimaginable. In the previous parts of this series, we explored how PureScript acts as a quiet rewrite of the Web , offering a territory of mathematical precision above JavaScript, and how its universal polymorphism allows it to target wildly different runtimes like Node.js, Erlang's BEAM, Chez Scheme, etc. But hey, what about the elephant in the room? Yes, what is running the majority of the web, today, in the shadow? You know. The eternal one, the big one, the one whose death everyone talks about, but who never truly dies. PHP. While the modern JavaScript ecosystem (among others) races toward edge computing and containerized microservices, around 70% of the web is still powered by PHP . From legacy WordPress sites to affordable shared hosting (cPanel, OVH, GoDaddy, basic LAMP stacks), a massive portion of the internet lives in environments where escaping the PHP runtime is either financially unviable, technically impossible, or simply unnecessary. And yet... For a long time, the PHP ecosystem has been left without a true, strictly typed functional and safe alternative, like TypeScript for Javascript. Even though recent PHP versions introduced fantastic features (short closures, match expressions, Fibers), the frustration that comes with its historical limitations and dynamic typing remains very real for some of its users. Very well! This is why I built phpurs : a brand new compiler backend that transpiles PureScript directly to modern PHP 8.4+ syntax. It is no longer a proof of concept. It is passing the official PureScript test suite , and it is ready for production. Here is how we bring the elegance of purely functional programming to the big web, while keeping deployment as simple as a legacy FTP upload, or anything alike. The easy deployment paradox In modern DevOps, deplo

2026-07-21 原文 →
AI 资讯

How We Distribute Video Events Across Regions With NATS JetStream

When a new video shows up in one of our regional crawlers, three things need to happen almost immediately: the SQLite FTS5 search index for that region needs a new row, the discovery ranking cache needs to be invalidated, and the sitemap generator needs to know a URL was born. For a long time we did all of this inline, inside the same cron process that fetched the video. It worked until it didn't. A slow FTS5 rebuild would stall the fetch loop, a sitemap write would fail silently, and a crash halfway through meant one region had the video indexed and another didn't. The fetch and the fan-out were fused together, and every failure was a partial failure. The fix was to stop treating "a video was discovered" as a function call and start treating it as an event. At TrendVidStream we run discovery across 8 regions, and the moment we introduced NATS JetStream as the spine between the crawler and the downstream consumers, the whole system got calmer. This post is the concrete version of how we did it: the stream config, the publishers, the consumers, and the mistakes we made that you can skip. Why Not Just Use a Queue Table in SQLite We already had SQLite everywhere, so the obvious move was a jobs table. We tried it. The problems showed up fast: Polling latency vs. load tradeoff. Poll every second and you hammer the DB with mostly-empty SELECT queries across 8 regions. Poll every 30 seconds and your search index lags noticeably behind your crawler. No fan-out. One row, one worker. If the sitemap generator and the FTS5 indexer both need the same event, you either duplicate rows or invent a consumed_by bitmask. Both are ugly. Locking. SQLite's writer lock means the queue table and the actual data table start contending under the multi-region cron bursts we run. Cross-region delivery. Our regions aren't all on the same box. A queue table doesn't cross machines without you building a replication story on top. JetStream solves all four: push-based delivery (no polling), multipl

2026-07-20 原文 →
AI 资讯

Building a Lightweight WooCommerce Product & Category Slider

When I started working on a WooCommerce slider plugin, I noticed that many existing solutions were packed with features that a lot of websites simply don’t need. More features often mean more CSS, more JavaScript, and a bigger impact on performance. So I asked myself a simple question: What would a WooCommerce slider look like if performance came first? My goals Instead of creating another all-in-one slider plugin, I focused on a few principles: Lightweight codebase Fast loading times Responsive by default Easy integration with Gutenberg Elementor support Shortcode support Clean and maintainable architecture Performance matters Every additional request and every unnecessary asset affects page speed. Some optimizations I implemented include: Assets are loaded only when required. Local libraries instead of unnecessary external requests. Server-side rendering where appropriate. Clean HTML output. Developer experience I also wanted the plugin to be simple for users. Instead of a complicated interface, the goal was: Install Create a slider Insert it into a page Done Lessons learned Building a public WordPress plugin taught me a lot: Documentation is almost as important as the code. User feedback quickly reveals edge cases you never considered. Keeping the codebase simple often leads to better long-term maintainability. What’s next? I’m continuing to improve the plugin by adding new features while keeping performance as the top priority. I’d also love to hear how other developers approach WordPress plugin development and performance optimization. Thanks for reading! ⸻ If you’re interested, you can check out my project here: WordPress.org: https://wordpress.org/plugins/amitry-product-category-slider/ GitHub: https://github.com/amitry-de/amitry-product-category-slider Live Demo: https://slider.amitry.de/

2026-07-19 原文 →
AI 资讯

stoneChat: An LLM Web Chat Built Natively for IE6/Windows XP

stoneChat is a locally hosted LLM web chat client built specifically for Windows XP-era machines and Internet Explorer 6. The frontend remains compatible with IE6. A small PHP server handles the chat requests, while stunnel provides HTTPS support for modern API endpoints. Models are defined in an INI file. Each model can have its own API endpoint, API key, model ID, streaming setting, token limit, and timeout. OpenAI-compatible APIs are supported. The same configuration file also manages users, model access, language preferences, and keyboard behavior. stoneChat runs on Windows with PHP 5.4 or newer and starts through RUN.cmd. GitHub: https://github.com/Water-Run/stoneChat License: WTFPL

2026-07-18 原文 →
AI 资讯

Distill Coding Agent Learnings

Repo: https://github.com/voku/agent-loop Demo: https://voku.github.io/agent_loop_demo/ Your Coding Agent Doesn’t Need More Memory. It Needs a Governed Loop. Coding agents repeat mistakes. The obvious response is to give them more memory: MEMORY.md project-rules.md agent-notes.md lessons-learned.md MEMORY_FINAL.md Soon the agent receives old decisions, temporary workarounds, copied transcripts, abandoned ideas, and rules nobody remembers approving. It has more context. It does not necessarily have better context. At some point, memory becomes landfill. The problem is not that coding agents forget too much. The problem is that most workflows fail to distinguish between temporary context, evidence, proposed learning, and approved project guidance. A transcript is not memory. A note is not a rule. A finding is not guidance. And a successful patch is not automatically a project convention. Instead of giving the agent one growing pile of context, I built voku/agent-loop around a governed workflow: task -> approved plan -> selective recall -> implementation -> verification -> recorded evidence -> reviewed learning Start with approved scope A coding agent should not begin by reading a ticket and creatively filling in everything the ticket forgot to mention. It should begin with an explicit work brief: goal; permitted scope; non-goals; affected files; required validation; human approval. For example: vendor/bin/agent-loop workflow plan PROJECT-123 \ --by lars \ --learning-root infra/doc/agent-learning \ --file src/Order/OrderService.php \ --file tests/Order/OrderServiceTest.php \ --goal "Reject invalid order state transitions" \ --scope "Order state validation and its tests" \ --non-goal "Do not redesign the order aggregate" \ --validate "composer phpstan" \ --validate "composer test" A human then approves that specific revision: vendor/bin/agent-loop workflow approve PROJECT-123 --by lars When the plan changes, the old revision becomes superseded , and the new one requires

2026-07-16 原文 →
AI 资讯

Don't apply WordPress major releases on day one — the "x.0.1 rule" and a calibration framework

The companion to the seven things to check before a WordPress major upgrade is the question that comes right after: when do you actually apply it? A new WordPress major drops today. Do you ship it to production tonight? Tomorrow? In a week? Hold for the next scheduled monthly maintenance? This call tends to live in tribal knowledge, but a few clear axes combined together give you a calibration framework you can apply every time without re-deciding from scratch. Here are five axes worth using. Premise — majors are not security patches The first thing to anchor: a major upgrade is not a security patch . WordPress ships security fixes via minor releases ( 6.4.1 → 6.4.2 , 6.5.0 → 6.5.1 — the second-digit bumps ). Those are same-day apply by default . Major releases ( 6.4 → 6.5 , eventually some 6.x → 7.0 ) carry new features and API changes; they aren't released to be applied immediately for security reasons. Without this distinction, the felt urgency of " we have to apply this for security " pushes you to rush majors that should wait. Minors immediately, majors by judgment — that separation is the first rule worth writing down. Axis 1 — wait for x.0.1 For essentially every major release, x.0.1 (the first patch release) lands within 1–3 weeks and absorbs the critical bugs that surfaced after launch. Past examples have included things like "the admin goes white under specific settings," "a particular theme breaks the block editor," "DB migration stalls in specific environments." Nobody hit these on launch day; they emerged as the world started using it for days or weeks. Just waiting for x.0.1 instead of x.0 sidesteps most of those launch-window bugs. For the first few weeks after a major lands, the world's WordPress installations are effectively running the beta test . Being downstream of the people who hit the mines is the rational position for a maintenance practice. Axis 2 — wait until major plugin vendors update "Tested up to" The thing that breaks most after a majo

2026-07-16 原文 →
AI 资讯

Seven things to check before a WordPress major upgrade — before "patch what breaks after" becomes a disaster

WordPress major version upgrades (5.x → 6.x, and eventually 6.x → 7.x) are a different animal from minor releases. Minor releases (like 6.4.1 → 6.4.2) are mostly bug fixes with low compatibility risk. Majors land API deprecations, raised PHP minimum requirements, and core block replacements all at once — and those things hit operations hard. The " just hit Update in the admin and patch whatever breaks " workflow can survive on a single personal site, but it tends to fall apart under multi-site maintenance — simultaneous failures across sites overwhelm root-cause triage. This post collects the things worth verifying before you run a major upgrade, as a seven-item checklist . 1. Has the minimum PHP version been raised? Major WordPress releases sometimes raise the minimum supported PHP version (6.6 lifted it to PHP 7.2.24, and a future 7.0 will very likely require PHP 8.x). What matters operationally isn't just the server's PHP version and WordPress's stated minimum — it's the intersection with the PHP versions your plugins and themes actually run on . You can usually upgrade your server's PHP, but older themes and plugins not running on new PHP isn't rare. A subtle failure mode here: traps like PHP 8.2+ deprecated warnings leaking into older WP-CLI JSON output , where nothing visibly errors but your operational tooling silently breaks. Before upgrading, run wp plugin list --format=json on the production PHP environment and verify you're getting clean JSON. That one check catches a lot of post-upgrade pain. 2. Audit "Tested up to" for every plugin Each plugin's readme.txt carries a Tested up to: X.X line — the developer's declaration of the highest WordPress version they've actually tested against . It's the first signal for major-upgrade compatibility audits. WP-CLI gives you the inventory in one shot: wp plugin list --fields = name,version,update_version,update --format = table Plugins where "Tested up to" is old AND no updates in the last year deserve scrutiny. Acti

2026-07-15 原文 →
AI 资讯

Manticore Speaks MySQL - So I Made It a Laravel Database Driver Instead of a Scout Engine

The problem I've been working with Manticore Search for about two years at EricaPRO , building the search layer for two financial data platforms. For the past year, that work has been a Laravel API. Manticore was never the problem. It's fast and it's stable. The problem was the gap between Manticore and Laravel. I had already built a package for this — laravel-manticore-search , a fluent builder over Manticore's HTTP/JSON API. It works, and it's still in use. But it's a client wrapper. Every feature had to be implemented manually. Every new filter or facet meant more custom architecture around the client, and none of the things Laravel gives you for free — models, migrations, pagination, casts — applied to it. Scout doesn't close that gap either. Scout gives you search() . No full query builder, no migrations for your indexes, sync is your problem. That's not a criticism — Scout abstracts over engines with completely different APIs, so it exposes the lowest common denominator. It just wasn't what I needed. What I needed was simple to describe and annoying to not have: something plug and play. Something Laravel way. Point Eloquent at Manticore and use Eloquent. The insight Manticore speaks the MySQL wire protocol. Out of the box, port 9306. You can connect to it with any MySQL client and run SQL. I had been using that port for two years without thinking about what it meant for Laravel. Because here's the thing: all of Eloquent — models, query builder, migrations, pagination, chunking — sits on top of a Connection and a Grammar . The grammar compiles builder calls into SQL for a specific dialect. That's the entire mechanism behind Laravel supporting MySQL, Postgres, SQLite and SQL Server with one codebase. So the real question was never "how do I re-implement Eloquent on top of Manticore's client?" It was "how thin can a Manticore grammar be?" If Manticore accepts MySQL-protocol connections and mostly-MySQL SQL, then a Laravel database driver — a custom connection plu

2026-07-14 原文 →
AI 资讯

Getting the public IP in PHP — no dependencies, no API key

Getting the public IP in PHP — no dependencies, no API key PHP is still one of the most widely deployed server-side languages, running a significant share of the web's backend code. If you're building a PHP application that needs the public IP address — for geolocation, DDNS, diagnostics, or country detection — this article covers the common patterns using IPPubblico.org : free, no key, HTTPS, JSON and plain text endpoints. Use case 1 — Your server's own public IP (one-liner) The simplest case: a PHP script that needs to know its own public IP. <?php $ip = trim ( file_get_contents ( 'https://ipv4.ippubblico.org/' )); echo $ip ; // 203.0.113.42 file_get_contents works if allow_url_fopen is enabled (it is by default on most servers). If not, use cURL (see below). Use case 2 — With cURL (recommended for production) file_get_contents has no timeout control and minimal error handling. For production code, cURL is better: <?php function getPublicIP (): ?string { $ch = curl_init ( 'https://ipv4.ippubblico.org/' ); curl_setopt_array ( $ch , [ CURLOPT_RETURNTRANSFER => true , CURLOPT_TIMEOUT => 5 , CURLOPT_FOLLOWLOCATION => true , CURLOPT_SSL_VERIFYPEER => true , ]); $response = curl_exec ( $ch ); $httpCode = curl_getinfo ( $ch , CURLINFO_HTTP_CODE ); curl_close ( $ch ); if ( $response === false || $httpCode !== 200 ) { return null ; } return trim ( $response ); } $ip = getPublicIP (); echo $ip ?? 'Unavailable' ; Use case 3 — Full geolocation data When you need country, city, ISP and timezone in addition to the IP: <?php function getIPInfo ( ?string $ip = null ): ?array { $url = 'https://ippubblico.org/?api=1' ; if ( $ip !== null ) { $url . = '&ip=' . urlencode ( $ip ); } $ch = curl_init ( $url ); curl_setopt_array ( $ch , [ CURLOPT_RETURNTRANSFER => true , CURLOPT_TIMEOUT => 5 , CURLOPT_SSL_VERIFYPEER => true , ]); $response = curl_exec ( $ch ); $httpCode = curl_getinfo ( $ch , CURLINFO_HTTP_CODE ); curl_close ( $ch ); if ( $response === false || $httpCode !== 200 ) { retur

2026-07-14 原文 →
AI 资讯

ScyllaDB PHP Driver 1.4.0: the extension is pure C23 now

The ScyllaDB PHP driver is not a C++ extension anymore. As of 1.4.0 it's pure C23, the ZendCPP template layer we leaned on for the object embed and allocate pattern is deleted, and the build no longer needs a C++ compiler at all. Every hand-written .cpp file is a .c file now (71 of them), the descriptor generator emits .c , and CMake builds with c_std_23 and nothing else. That's the biggest change to how this extension is built since we forked it for PHP 8.0. This is also the release where a plan I opened back in 2023 finally landed. PR #50 laid it out: rewrite the src/Cluster directory to be more maintainable, use Zend Fast Argument Parsing, remove some memory allocations, and add .stub.php files that generate the C headers so nobody has to hand-maintain Zend arginfo by hand. 1.4.0 is that plan finished, and a lot more that grew out of it. The things you'll actually feel: persistent session connect() doesn't allocate a 200-character key string on every call anymore, and the minimum PHP is now 8.3 (8.2 is gone). Nothing in your application code changes, this is almost all under the surface. The .stub.php Build The idea from PR #50 was small. Instead of writing ZEND_BEGIN_ARG_INFO_EX blocks by hand and keeping them in sync with the actual method bodies, write the signature once in a .stub.php file and generate the C arginfo from it. In v1.4.0 that's the whole build. There are 75 .stub.php files now, and each one is just the PHP signature of the class: // src/Keyspace.stub.php interface Keyspace { public function name (): string ; public function replicationClassName (): string ; /** @return array<string, mixed> */ public function replicationOptions (): array ; public function hasDurableWrites (): bool ; /** @return Table|false */ public function table ( string $name ): Table | false ; public function aggregate ( string $name , mixed ... $types ): Aggregate | false ; } At build time CMake runs gen_stub.php (vendored from PHP 8.5's build/gen_stub.php , with two small p

2026-07-14 原文 →
AI 资讯

PHP Speaks QUIC Now, and OpenSSL Did the Hard Part

I released php-quic 1.0.0, a PHP extension that gives you raw QUIC transport with first-class access to streams. It links against the same OpenSSL that PHP already links against, and that is the whole trick. Why This Was Awkward Before QUIC is not a protocol you bolt on in userland. It is TLS 1.3, congestion control, loss recovery, and stream multiplexing, all riding on UDP, and all of it has to be right before you can send a single useful byte. You could get there from PHP if you were willing to bind to a foreign QUIC library like ngtcp2 or quiche through FFI. That works, but now your PHP app carries a second TLS stack, a second set of CVEs to track, a build story that involves Rust, and a version matrix that has nothing to do with the one your distro maintains. For a language whose entire deployment story is "the package manager handles it," that is a lot of rope. What Changed OpenSSL 3.5 shipped a native QUIC stack. Client and server, in the library PHP is already built against. That reframes the problem. The extension is no longer "embed a QUIC implementation into PHP." It is "expose the QUIC that is already sitting there." No new TLS stack, no FFI layer, no Rust toolchain in your build. If your OpenSSL is patched, your QUIC is patched. The requirements fall out of that directly: PHP 8.4 or newer (8.5 on Windows), NTS or ZTS OpenSSL 3.5.0 or newer, built with QUIC support Transport, Not HTTP/3 The thing I most wanted to avoid was shipping an HTTP/3 client and calling it a QUIC library. QUIC is a transport. HTTP/3 is one protocol that runs on it, and it is not the interesting one for everybody. DNS-over-QUIC runs on it. So does anything you want to invent that needs multiplexed, ordered, loss-recovered streams without head-of-line blocking across them. So php-quic hands you connections and streams, and stays out of the framing business. If you want HTTP/3, you build HEADERS frames and QPACK on top of it, and the README has a worked example. If you want something

2026-07-14 原文 →
AI 资讯

Browsershot Alternatives: HTML to Image in Laravel Without Puppeteer

Cross-posted from the HTML to Image blog , where the original lives. Browsershot is the package most Laravel developers reach for when they need to turn HTML into an image. It wraps Puppeteer, drives real Chrome and produces pixel-accurate output. On your machine it works first time. Then you deploy, and the first render throws Failed to launch the browser process! . The problem is not Browsershot's code. The problem is what it demands from the machine it runs on. What Browsershot actually asks of your server Browsershot is a PHP package with a second runtime hiding inside it. To run it in production you need Node.js, the Puppeteer npm package, a Chrome or Chromium binary, the long tail of shared libraries Chrome links against ( libnss3 , libatk , libgbm and friends on a slim Debian image) and a font set wide enough to cover whatever your templates contain, emoji included. That is manageable on a full VPS you control. It falls apart in the places Laravel apps increasingly run: Laravel Vapor and serverless. The PHP Lambda runtime ships neither Node nor Chrome, and you cannot apt-get your way out of a Lambda. The Puppeteer on Lambda guide covers just how deep that particular hole goes. Shared and managed hosting. No root, no system packages, no browser binary. Browsershot is simply off the table. Slim Docker images. php:8.3-fpm-alpine carries none of Chrome's dependencies. Adding Chromium, its libraries and fonts costs a few hundred megabytes and a permanent maintenance line in your Dockerfile. CI pipelines , where every job downloads a browser before your test suite can touch a render. The dependency does not stay contained either. Even Spatie's newer packages inherit it: spatie/laravel-og-image renders through laravel-screenshot , which drives Browsershot underneath, so the Node and Chrome requirement follows the whole family wherever it goes. The usual workarounds The first workaround is the fat container: bake Chromium, the shared libraries and a font stack into y

2026-07-11 原文 →
AI 资讯

A Free CBT Equation Editor for Math & Chemistry

While building a Computer-Based Testing (CBT) platform, I ran into an unexpected problem. Creating mathematics and chemistry questions wasn't nearly as straightforward as I expected. Although there are many excellent editors and open-source libraries available, I couldn't find one that brought everything together in a way that was simple, lightweight, and designed specifically for CBT systems. Instead of building everything from scratch, I took a different approach. I combined several powerful open-source technologies into a single editor that focuses on one job—making it easy to create mathematics and chemistry content for online examinations. The result is CBT Editor, a free and open-source equation editor built for developers, schools, and educational platforms. It supports common mathematical expressions, chemistry notation, scientific symbols, fractions, superscripts, subscripts, and more, while remaining easy to integrate into existing projects. This project isn't meant to replace the fantastic libraries that already exist. In fact, it depends on them. The goal is to provide a clean, unified experience so developers don't have to spend hours combining multiple tools just to support technical examination questions. If you're building a CBT platform, an online examination system, or any educational application that requires mathematics and chemistry editing, I'd love for you to give CBT Editor a try and share your feedback. 🔗 Live Demo: https://holygist.github.io/cbt-editor/ Open-source software grows through collaboration. If you find the project useful, feel free to contribute, report issues, suggest improvements, or simply share it with other developers.

2026-07-11 原文 →
AI 资讯

Dev Log: 2026-07-09 — one source of truth, three times over

TL;DR Three unrelated repos, one recurring theme: derive from a single source of truth instead of duplicating it. Shipped a registry-driven sidebar section switcher (public), converged a multi-system password flow, and pushed on a customer-data identity engine. Details on the first two live in their own posts today. 1. Registry-driven sidebar switcher (public) Added a section switcher to the kickoff starter kit. The sidebar, the switcher, and breadcrumbs all read the same config/menu.php list, and the active section is picked by longest URL-prefix match — so a detail page like /admin/roles/42/edit keeps its parent selected. Full write-up in the focused post. 2. One canonical password flow Converged two apps that each rolled their own password-change/reset logic onto a single shared engine, with a fixed order (directory → external DB → local app) and no config-toggle to skip backends. A password that syncs to some systems is worse than one that fails outright, so partial success is now impossible by construction. Also fixed a subtle status bug — an unreachable backend reports skipped (a runtime fact), not disabled (a config state that no longer exists) — and added an audit log so "did it sync?" is a query, not a guess. Separate post today goes deeper. 3. Identity resolution engine (customer data work) Steady progress on a CDP-style identity layer: an idempotent, header-versioned ingest endpoint that queues incoming records, then a resolution engine that can resolve, merge, unmerge, and quarantine profiles. Two things I care about here: PII handling: sensitive identifiers are encrypted at rest with a blind index for lookups, and masked in audit trails — you can search on a value without storing it in the clear. Right-to-erasure: an erasure cascade plus an erasure log, so a deletion request actually propagates and leaves a defensible record that it did. ingest -> queue -> resolve -> profile | +-> merge / unmerge / quarantine No code from this one here — it's teaching t

2026-07-09 原文 →
AI 资讯

When a password sync is 'partly done', it's a bug: converging on one canonical flow

TL;DR Two apps each had their own password-change/reset logic, plus config toggles to enable/disable backends. That combination quietly allowed partial syncs. Fix: one shared engine, one canonical order , backends mandatory (no config-disable), and every attempt written to an audit log. Lesson: for a write that spans several systems, "configurable steps" is a footgun. Make the flow fixed and make failure loud. The setup A user changes their password. Behind the scenes that single password has to land in several systems — a directory, an external database, and the app's own store. Two separate apps were doing this, each with slightly different code, and each with config flags like sync_oracle => true|false to turn backends on and off. Sounds flexible. It's actually a trap. Why configurable backends are a footgun TL;DR: a password that updates 2 of 3 systems is worse than one that updates none — because now the systems disagree and nobody gets an error. The moment a backend is optionally skippable, "skipped" and "failed" blur together. Someone flips a flag in one environment, forgets it in another, and now prod and staging run different flows. Debugging a login failure means first reverse-engineering which steps actually ran. Before After Each app had its own reset logic One shared engine, both apps call it Backends toggle via config Backends are mandatory, always run Order implicit / differed per app One canonical order: New directory → external DB → local app "Did it sync?" answered by guessing Every attempt logged with per-service status The canonical order The order isn't cosmetic. It runs most-authoritative-first, so if a downstream step fails you haven't already told the user their new password works. change/reset request | v [ New Directory ] --ok--> [ External DB ] --ok--> [ Local App store ] | | | fail fail fail | | | +----> stop, record per-service status, surface the failure Same engine, same order, both apps. A change API and a reset flow are just two entr

2026-07-09 原文 →
AI 资讯

A pending-plugin-count badge on the 🔌 button — reusing the dashboard cache instead of doubling state

A client asked: " After I run a cross-site update check, can each site show — right in the site list — how many plugin updates are still pending? " Visually the answer was obvious: a small red badge on the top-right of the 🔌 plugins button, like an unread-notification count. Easy to specify. The harder question was where the data comes from . We could have added a fresh API endpoint and a new cache to hold "pending count per site." But doing that would have doubled state management , and we already had a cache that knew this. We routed through the existing one. Here's the reasoning behind that decision. Reuse the dashboard cache as the data source The cross-site updates dashboard (the one we wrote about in killing the 24.5-second silence with a cache-first design ) already kept each site's pending plugins in a localStorage-backed state called _updatesDashState . Its shape: _updatesDashState = { sites : [ { site_id : " abc... " , plugins : [ {...}, {...}, {...} ] }, { site_id : " def... " , plugins : [ ... ] }, ], total_pending_count : 12 , loadedAt : 1748600000000 , } Look up by site_id , take plugins.length , and you have the badge's number. No new API, no new cache. The data that powers the cross-site dashboard is also the data that powers the site-list badge. The win of not adding state is quiet but real: When a maintenance run invalidates _updatesDashState , the badge disappears automatically (no sync code to write) The TTL (originally 7 days; later extended to 30 days with partial invalidation ) inherits from the existing design The badge and the underlying count can't drift — there's no second copy to fall out of step There's always a temptation to spin up a new endpoint for a new UI element. The rule we settled on: if the existing state answers it, don't add more. Attaching the badge — consolidate into helpers Both the list view and grid view need the same badge on the 🔌 button, so the logic lives in helpers. function _getPendingPluginCountForSite ( siteId )

2026-07-08 原文 →
AI 资讯

Dev Log: 2026-07-07

TL;DR Collapsed a billing model from à-la-carte features to plans-only, in four safe phases. Unified authorization across web, API, and MCP so all three obey one permission layer. Fixed a legacy Oracle password-sync writing to the wrong column. Four repos moved today. Here's the thread that ties most of them together: one source of truth beats two. Billing: plans-only Spent most of the day migrating a SaaS off per-feature à-la-carte subscriptions and onto plans-only entitlements. The interesting part isn't the model — it's doing it without a billing outage: seed plans, switch reads to plans, backfill every org, then delete the old machinery. Expand/contract, four deployable phases. Full write-up in the focused post. One permission layer for three surfaces An ops tool exposed the same actions three ways — web UI, API, and an MCP server for agent access. The bug: each surface checked authorization slightly differently, so an MCP tool could allow something the web UI blocked. The fix was to make the MCP tools gate on the same permission layer as everything else, so: web ─┐ API ─┼─► one permission check ─► allow / deny MCP ─┘ TL;DR: web ≡ API ≡ MCP — three doors, one lock. Also added a dedicated support-engineer role scoped for debugging without handing over the keys to everything, plus identity/diagnostics/SLA read tools so an agent can answer "why didn't this notification send?" without shell access. Before After Each surface authorizes its own way Single permission check, shared MCP tool could out-permission the UI MCP bound to the same guard No debug-scoped role support_engineer role, read-only diagnostics Legacy Oracle password sync Smaller but sharp: a password reset was writing to the wrong Oracle column and also touching a date_modified field it had no business updating. Routed the student reset to the correct password column and dropped the stray write. Lesson with legacy schemas — the column that looks right and the column the app actually reads from are not a

2026-07-08 原文 →