Официальный сайт SLAED CMS
Журнал изменений
The Phoenix collection of 2026-09-08 is removed whole and replaced by eighteen presentations of index.php?name=main that share one engine, deck.css and deck.js, and differ in page skeleton, first screen, chart mix and palette rather than in a preset. Six faces develop the leader 00-dashboard-layout.html; twelve reinterpret the dashboard references. A nineteenth face puts the leader into the chrome of the system.
Core changes:
- Presentation engine (demo/assets/deck.css, demo/assets/deck.js):
One palette contract on .p-page, every token in both schemes through light-dark()
- grid, cards, KPI tiles, tables, rings, bars, heat, logs, ticker, nav, rail, topbar, road, timeline,
calendar, showcase, voices, scenes
One body of figures, DECK_DATA, and every moving part read from data attributes
- canvas charts: area, line, spark, bars, hbars, donut, gauge, radar, candles, heat, with periods
and hover tips
- module registry table with search, header sort and section filters
- event streams, flicker figures, CSS bars, heat cells, calendar, written lists
- guard and cache switches, the guard court, the cache route, the PDO bus, walkers, scroll spy
- the real site header and footer fetched around a face unless the gallery opens it bare
- Faces (demo/01-command-deck.html … demo/18-aeros-horizon.html):
01-06 keep the leader's DNA in six compositions: command deck, observatory wall, ledger with a right column, cockpit with three gauges, one-column flow, module studio
07-18 follow the references: RecehTok neon and exchange, Calltimes rhythm, Vaulto carbon and editorial, Hynex vital, Fobework campus, odds analytics, cyber sentinel, Rinesk lime, Aeros atmosphere and horizon
- real facts stay fixed, illustrative figures are declared on every page
- Laboratory face (demo/19-phoenix-laboratory.html, demo/assets/nineteen.css, demo/assets/nineteen.js):
- the leader shown inside the chrome of the system
- Stand chrome (demo/assets/demo.js, demo/index.html, demo/README.md):
- the manifest names the scheme a face was designed in and the gallery opens it in that scheme
- the README describes the engine, its attributes and the nineteen faces
- Documentation (docs/CABINET-CONSOLIDATION-2026.md):
- the finished consolidation plan is removed from the tree
Benefits:
- a new face is markup, a palette and v-* rules, never a script of its own
- eighteen pages cannot disagree about a figure
- both schemes are held by one declaration per token
Technical notes:
- nothing here ships: demo/ is the development stand
- no external resources; icons, photos and screenshots are local
- screenshots keep the two laws of the stand: 260 wide, never taller than natural
The first series of the demo stand is rebuilt: the twenty-three variants of the main page are replaced by the original dashboard layout as the reference and eighteen presentations of SLAED CMS 8, each a standalone dashboard composition on the lite tokens with a local palette and the shared presentation shell. The private-message config takes a shorter page.
Core changes:
Demo stand (demo/00-dashboard-layout.html, demo/01-phoenix-command.html ... demo/18-aeros-horizon.html, demo/assets/presentation.css, presentation.js, presentation-shell.js, demo/index.html, demo/assets/demo.js, demo/README.md):
the old variants 01-23 are removed; the gallery has four sections and lists the reference 00 with the new presentations
- the presentations share presentation.css and presentation.js and keep their palette local
- Private messages (config/privat.php):
- num 25 to 15 and nump 10 to 5: fifteen messages and five page numbers per mailbox page
Benefits:
- the stand compares dashboard compositions of the product, not texts
Technical notes:
config/privat.php is a shipped default; a compiled config/local.php on a stand keeps its own values until it is rebuilt
The six batches of docs/CABINET-CONSOLIDATION-2026.md land together. Every panel, tile, feed, hub and shelf of the cabinet opens with the same head fragment, every list row is the cabinet row, every fold runs through the toggle mechanic of slaed.js, every icon name PHP writes comes from one map, and the notices of the private-message and favourites pages stand in one slot. Nothing a reader does on these pages changes; what changes is that there is one way to build them.
Core changes:
- One head (templates/lite/fragments/panel-head.html, theme.css, tools/ui-contract.php):
the fragment takes icon, title or title_html, title_id, hint, fold, chips, total_html and live_title and renders div.sl-profile-hub-head > h3 + span.sl-profile-hub-total
- nineteen heads of three forms are replaced: the settings tile head, the hub head of the
profile and the favourites tiles, and the bare h3 headings of the cabinet home, the
profile panels and the activity feed
- .sl-opt-tile-head, .sl-cab-head and .sl-profile-head leave the CSS; the margin under a
head is contextual to the panel, the feed and the deck
- the shelf head carries fold, hint and its count chip; the profile head carries the
action dial and the share dial as total_html
- One row (theme.css, fragments/privat-row.html, partials/account-profile*.html):
.sl-cab-row is the base; .sl-profile-entry is the modifier that opens the leading track for a date or a count chip; .sl-pmf-item is the modifier of the message row with its own tracks, padding and states
- .sl-profile-info-row stays a component of its own, a label beside a value
- .sl-cab-row.sl-profile-hub-row takes the hub-row height the shadowed rule never reached
- One fold (fragments/privat-focus.html, core/user.php, theme.css):
the focus deck folds through data-sl-toggle under the shared head, with the chevron pair and a remembered state; the deck reopens as flex through data-sl-toggle-display
- the seven summary rules of the details are gone and no <details> is left in the theme
One icon map (core/system.php, core/user.php, core/admin.php, modules/account/index.php, admin/modules/modules.php, tests/Support/contract_probe.php):
getIconName() beside getModuleName() maps module keys and concept keys to Bootstrap Icons glyphs, falls back to the module config and then to puzzle
eighty-two 'icon' => literals in core, modules and admin read the map; getFavoriteIcon() is removed; the glyphs of eleven pages are the same before and after
Small safe ones (fragments/opt-lamps.html, fragments/cabinet-notes.html, core/user.php, modules/account/index.php, partials/account-*.html, theme.css):
the lamp row of the settings and favourites pages is one fragment; a non-meter lamp carries its value in title
the rail chip is the ordinary flex chip again with its label in a span that ellipsises; the block variant and its hand-set icon margin are gone
- addPrivateMessage() passes one error as a string, an array is for several messages
the notice slot is #cabnotes through getCabinetNotes(); the favourites page prints it under the nav, and an emptied list answers the slot out of band instead of an alert in the list
Clean-up (core/helpers.php, fragments/pager.html, lang/.php, admin/info//ru.md, tools/ui-audit-baseline.json):
the pager summary reads "Страница P из M. Всего: N" through _PAGE, _NUMOF and _OVERALL on every lite pager; _BY and _PAGE_S leave the six locales
- three duplicate allowances of the contract that named removed rules are dropped
- the help of private messages and favourites describes the pages as they are
- the audit baseline is stored: scoped 92 to 89, nothing grew
Benefits:
- a new cabinet panel is a head include and cabinet rows, not a third markup
- theme.css is 62 lines shorter and the head, row and fold rules live in one place each
- an icon is renamed in one line for the whole site and the panel
Technical notes:
- the settings tile head is an h3 now, with the same id for aria-labelledby
every head has a 34 px floor, so tile and panel heads grew by 8 px; the hub head takes the panel margin and the hub row is 46 px, as its token always said
the pager data no longer carries by, page_s, perpage or pages; page_label and page_of replace them
The favourites page is rebuilt after the "shelves" stand: one shelf per module with a remembered fold, a lamp row, and a search field with module chips that filter over htmx without a reload. The private-message page gains a notice slot under the focus deck, a confirm dialog that holds the htmx request until the answer, a filter bar of one standard for every mailbox, and a pager whose setting names what the reader counts. Counts, chip gaps, alert lists and icon names are unified across the system.
Core changes:
- Favourites (core/user.php, templates/lite/partials/account-favorites*.html):
getFavoriteList() reads every favourite of the member in one pass and renders the shelves partial
- lamps: quota with the free count, module count, module with the most, latest bookmark with its date
- search field and module chips call go=1&op=getFavoriteList&part=shelves and swap #favshelves, the tally and the chips arrive out of band
- matches are highlighted through filterTextHighlight(); q is read with getVar(..., 'word') so Cyrillic and phrases search
- shelves fold through the data-sl-toggle mechanic with a remembered state and swapped chevrons
- getFavoriteIcon() is shared with the cabinet home
- _favorites gains a nullable time column (setup/sql/table.sql, table_update6_3.sql); addFavorite() writes NOW()
- Private messages (core/user.php, index.php, plugins/system/slaed.js, templates/lite/partials/privat-*.html, fragments/privat-notes.html):
- every notice stands in #prnotes under the focus deck: getPrivateMessageView() wraps getPrivateMessagePane() and appends the slot out of band; refusals and the mailbox quota use the error tone
- data-sl-confirm on an htmx element goes through htmx:confirm and issueRequest(); the dial's delete no longer fires before the dialog nor follows a token-less address after it
- one filter bar for every mailbox: all, unread, read, week, older than a month, a reset chip and the shown range or the found count in #prshown
- the bulk bar loses its label and is centred; the letter chips read Прочитано, Профиль, Сайт; the interval chip stands in the recipient card in the warning tone with a grade glyph per mailbox state
- the right column takes the 12 px inset of a tile; the list row no longer overflows its column; the reply form reaches the frame
- the AJAX debug panel runs through getTimedHtml(), so the SLDBG marker is replaced
- Shared (core/helpers.php, lang/.php, templates//assets/css/theme.css, tools/ui-contract.php):
- getTplPagerView() keeps a window of exactly maxpg numbers with the first and last page as anchors
- one global _NUMOF for "N of M" replaces _EDITOR_QUOTA and _PRQUOTA; FAVOR* constants for the new page in six locales
- the chip icon gap is 4 px everywhere; alert lists carry no marker; lamps keep to one line
- bi-person-badge becomes bi-person-vcard throughout
- Docs and tooling (docs/, admin/info/favorites/ru.md, tools/ui-shots.json, demo/):
- docs/CABINET-CONSOLIDATION-2026.md plans the consolidation of the cabinet templates and styles in six batches
- the screenshot manifest gains the favourites page; the favourites help doc describes the page without a pager
- the node docs and the favourites stand receive the author's working notes
Benefits:
- the favourites page answers what a bookmark is and where it lives, and searches without leaving the page
- a private-message action cannot leave before its confirmation, and its errors land in one visible place
- the cabinet reads one way: one count wording, one chip gap, one pager semantics
Technical notes:
- schema: _favorites.time DATETIME DEFAULT NULL; older rows keep NULL and the lamp names the module instead of a date
- the config setting favorites.num is no longer read by the cabinet page
- no new theme tokens; the duplicate allowlist in tools/ui-contract.php drops .sl-author from the author/meta/oauth group
The settings page is brought to the bridge variant of the stand with the mechanics the theme already carries, and what the page taught is folded back into the cabinet: one box, one head, one form row and one file window for every page under the account. The screenshot rig, which was the slowest gate of the work, walks its two modes side by side and stops waiting for a page that could never settle.
Core changes:
- Settings page (modules/account/index.php, partials/account-settings.html):
the lamps, the rail, the tiles, the save bar and the gallery follow set-10-bridge
- the rail marks stand at even fractions of the width, the first flush left and
the last flush right, and the road walked is a width the script writes
- the lamps paint their figure in the tone of a category, warning through the new
--sl-warning-strong step, because the base step fails AA for large bold type
a preset picked in the gallery saves itself over htmx through setavatar() and the toast confirms it; the form bar does not rise for a control that saved itself
- the journal tile and the profile ring are gone, the password tile takes the width
getFieldsInRows() gives the extra fields as data, and getTplFieldsIn() is that list folded into form rows for its five other callers
sixteen constants came and six went again: the hints under the questions and the heads of the tiles stay, the short rail captions did not
- Form rows (theme.css, base.css):
the caption column of a form is one column per form: .sl-form and .sl-opt-tile lay fit-content(--sl-form-label-width) and minmax(0, 1fr), and a row standing directly in them takes subgrid, so the captions line up on one edge and the fields on another; a browser without subgrid keeps the quarter
a row holding an editor folds on the mount point and no longer only after the script arrives, and a hint asks for no width of its own, so the caption column is not sized by either for the moments the parser stands on the editor scripts
the track of an editor row has a floor of zero: a bare 1fr could not shrink below the toolbar and made every page with a comment form scroll sideways at 768
Cabinet and profile (partials/account-home.html, account-profile.html, account-profile-feed.html, fragments/account-nav.html, theme.css):
the panel of the cabinet and of the profile is the settings tile, and every heading is the tile head with the chip of tone, joined by selector list
- the inner pages carry the full-size cabinet tiles, each with its subtitle
the activity feed's tabs wear the chip, the cabinet rows share the entry's metrics, and the hub of the profile is a scrolling feed of entries
the spinning indicator freezes with any hovered feed, and the scroll walks translateY(-50%) of the doubled feed instead of a distance measured before the avatars had loaded
the card and the deck wear the tile's box, the RSS field is the field, the empty badge is hidden, and a button carries a gap between its icon and its label
- Layout (layouts/app.html, home.html, theme.css):
the layout names the sides it rendered on #container as sl-has-left and sl-has-right, and the CSS reads them instead of :has(#sidebar): the aside stands after the content, and a first frame painted before the parser reached it laid the content over the full width and then pulled it back
- File manager window (partials/file-manager.html in both themes):
the window opened for a form field carries data-sl-window like the one opened from an editor: it drags, expands and stacks the same way; the two copies stay identical
- Screenshot rig (tools/ui-shots.mjs, tools/ui-shots.json, docs/TEMPLATES.md):
the two modes of the manifest walk side by side in contexts of their own, a full capture takes about five minutes instead of ten
- --only takes a comma list, so a change is re-compared on the pages it touched
the ticking counter of a live box is masked; it was why the article page never settled
- Stand (demo/fav-01..04, demo/assets/demo.js, demo/README.md):
a fourth series proposes four faces for the favourites page out of the parts the cabinet already has: shelves, tabs, bays and the table told what it lacked
- Node plan (docs/node/*):
the working list and the review of the package, and the plan documents adjusted after the review
Benefits:
One box, one head and one form row across settings, cabinet and profile, and the two file windows become one
Every cabinet page arrives without a layout shift; measured zero on messages, settings and favourites
The rig costs half the wall clock and reports a page that moved instead of one that never settled
Technical notes:
Two tokens join the API: --sl-warning-strong and --sl-form-label-width; the template-written --sl-d-at, --sl-d-of and --sl-d-spy are registered under data and --sl-d-distance leaves it
- tools/ui-contrast.json is regenerated from the walk after the palette changed
The constants _ACCOUNT_LOG, _ACCOUNT_FILLNOTE, _ACCOUNT_LINKED, _ACCOUNT_SIGNIN and _ACCOUNT_GALLERY are removed; a theme or a language file that named them will not find them
The walk drops the compiled configuration between rows, and on a checkout that is
what makes the server mint a new master secret and persist it into the tracked
config/security.php. The walk now guards that file, and prevents the mint rather
than only cleaning up after it.
Core changes:
- Guarding the shipped security file (tools/upload-route-check.php):
setConfigGuard() takes a snapshot before the sessions are opened and gives the file back through register_shutdown_function(), so an interrupted run leaves nothing behind either
the shipped secret is seeded with the live one for the length of the walk
- a token is derived before the request that carries it, and that request is the
one that rebuilds and mints, so cleaning up afterwards cannot save the answer
- measured: without the seed the isolated editor mode fails 21 rows
the merge is warmed by one request before the file goes back, because the walk leaves it deleted and the next visitor to the stand would otherwise mint the master minutes after the run reported a clean tree
one row asserts the restore and names the change in its detail; it never asserts the absence of a change, since both the seed and the mint are expected
Benefits:
A generated master secret can no longer reach a commit through this tool, which no gate in the tree would have caught
The editor and full modes are stable instead of depending on whether the shipped secret happened to be empty when the run started
Technical notes:
With no compiled configuration to read the live secret from there is nothing to seed with, and the walk then behaves as it did before
A process killed without running its shutdown handlers can still leave the seeded secret in place
Every document of the node design moves from a frame to an approved state. The status table of the directory turns from a list of open questions into a list with nothing left blocking approval, and the fifteen documents behind it are filled in to match.
Core changes:
- Directory status (docs/node/README.md):
- the plan reads "planning finished, implementation not started"
- every one of the fifteen documents is marked approved, with no blocker left
- document 12 is renamed to the boundary of the old data rather than its transfer
- Contracts filled in:
05-core-api.md: the public contracts and their signatures06-types.md: the type, field and capability model03-database.md: the extension tables and the remaining lifecycle transitions02-naming.md: the names of the remaining classes, methods and lang constants13-testing.md: what each layer is held by15-decisions.md: the log grows with the decisions the rest of the pass took- The remaining documents follow the same pass:
goals, code structure, routing, rendering, integrations, extensions, security and performance, migration boundary and roadmap
Benefits:
- The plan can be executed without reopening the questions it was written to close
- The decision log carries why each answer was chosen, not only what it is
Technical notes:
- Documentation only; no code of the subsystem exists yet
- Written in Russian, as the rest of this directory is
Profile discussions come back as an ordinary comment target rather than a
mechanism of their own: account joins the map the Comment class already drives,
so writing, moderation, censoring and the points award reach a profile through
the same path every other target uses.
Core changes:
- Comment targets (core/classes/comment.php):
accountenters the MODULES map with_usersand the points slot 3the target is excluded from the denormalised counter, because
_userscarries nocommentscolumn of its own- setTargetCount() and addTargetCount() refuse it
- the drift report skips it, so no repair is attempted on a column that is absent
getTargetMode() answers for a profile on its own branch: the row must exist, and
$conf['users']['prof']closes it to anyone who is not signed in- the site snapshot carries
profbeside the censor settings - Profile view (modules/account/index.php):
- view() renders the comment block for the profile it has just drawn
- Rating slots (core/system.php):
- the
accountrating reads the points slot 2 instead of 0 - Probes and tests (tests/):
- the comment write probe walks the new target beside the other eight
both counter expectations answer zero for
account, and the probe does not setacommon a table that has no such column
Benefits:
One write path for every comment target: a profile discussion is moderated, censored and credited exactly as a news discussion is
- No second counter to keep honest, because the profile target has none
Technical notes:
_usershas nocommentscolumn and noacommcolumn; both are handled by excluding the target rather than by adding columns- Guest access to a profile discussion follows
$conf['users']['prof']
Take the file manager out of the editor plugin and make it the one way the whole system asks for a file. A form row no longer carries a bare file field: it carries a button that opens the same window an editor opens, and what was picked comes back as a chip. The storage of already uploaded files becomes reachable from every place a file is asked for instead of only from inside an editor.
Core changes:
- Upload places (core/system.php):
getUploadPlaceRule() resolves one rule for one place, named with a dot
- the grammar ^[a-z0-9_]+\.[a-z0-9_]+$ lives here and in no caller
- three branches: <mod>.attach, files.dist, users.avatar
- answers every field the routes read, not only the limits
- the upload right is two settings ANDed: upload && add, upload && addquest
- users.avatar is a member place: guestupload and guestfiles are hard zero
mod is the module a place is moderated as, never the first segment: users.avatar answers account, and no caller splits the place string
ops names which of the four routes a place permits; a field place permits editorFiles alone, so a form place can never reach an upload or a deletion
- getUploadFileArea() replaces getEditorFileArea() and is built from the rule
- getUploadTakenFile() resolves one client path for both form handlers
- getUploadPlaceView() answers the module, the rights and the extension list
- Place routing (index.php, plugins/editors/toastui/driver.php):
- the go=4 entry guard reads place, raw: filterVar() empties a string with a dot
- getEditorRouteRule() gates on ops beside its three existing guards
every endpoint URL carries place instead of mod, built server side, so both ends of the contract migrate in one step and no shim is needed
- The window becomes the theme's (core/helpers.php, templates/{lite,admin}):
- getFileManagerWindow() builds it from the place rule, in either mode
getFileManagerField() builds the row: the button, the chip and the three hidden carriers the submit takes to the handler
- partials/editor-toastui-files.html becomes partials/file-manager.html
the window rules move from assets/editors/toastui/skin.css into theme.css under the new root sl-fm-win; the administrative browser owns the bare sl-fm-* names, so the window stays scoped rather than unscoped
- the shot panel rules move with them: getWindowShot() is already system-wide
- fragments/file-input.html is deleted; fragments/file-field.html replaces it
- The runtime leaves the plugin (plugins/system/filemanager.js):
editor-upload.js becomes plugins/system/filemanager.js and is delivered by getFileManagerWindow() once per request, so a page with no editor behaves
- addField(id, node, opt) is the second entry: a box instead of an editor
the eleven draw templates the runtime needs move to file-manager-templates.html; the four emoji ones stay with the driver
SlaedToastUi keeps tags, emoji and the word lists; editor-tags.js calls the new namespace explicitly and no alias is created
- The two callers (modules/files/index.php, modules/account/index.php):
both read the three outcomes in a fixed defensive order and resolve a stored path through getUploadTakenFile(), which refuses it unless the owner matches
the catalogue passes getEditorFileOwner() and never the integer user id, which used to turn every guest token into zero and match one guest against another
the avatar keeps plan 2's arbitration: a preset beats anything the window produced, and an avatar failure does not roll back the profile
- Tooling and tests:
tools/upload-route-check.php walks the new place routes; its merge deletion is verified rather than assumed, and the master secret is re-read per call while the last non-empty value is held
- tools/ui-contract.php follows the rules into the theme
Benefits:
One door: adding a file is one gesture everywhere, and stored files are reachable from every place a file is asked for
One rule per place instead of a rule assembled by hand in each module, so a limit and a right are answered in one file and read the same before and after
The server refuses what the interface does not draw: an interface that draws no button is not a guard, and the ops gate is enforced in one place
- Guest uploads are isolated per session instead of sharing the owner segment 0
Technical notes:
Breaking: the editor endpoints take place=<mod>.<slot> and no longer accept mod=<mod>; a dot cannot travel through filterVar(), which is why the parameter is new rather than widened. Both ends are built server side and migrate together
Breaking: _FILE_USER, _AVATAR_USER and _EDITOR_ADD are removed from lang. The first two lost their callers with the rows they captioned; the third was a scoped duplicate of the global _ADD, identical in all six locales
Breaking: templates/{lite,admin}/fragments/file-input.html is deleted. A theme or module calling it must move to file-field.html through getFileManagerField()
sl-toastui-upload survives as a second class on the window: it cures the fixed button height the vendor root imposes and belongs where the vendor is
data-editor is deliberately not renamed: it is read by five producers and a template-only rename would break the editor silently
Files stored before this change keep the owner segment 0 and are listed to nobody; they are not migrated, which is the correct failure direction
The first two plans of the queue are finished and each deleted itself with its last batch, but the third still named both by path. A reader following the reading order would open the plan, go looking for its two prerequisites and find neither.
Core changes:
- Queue section (docs/UPLOAD-FIELD-2026.md):
- the two finished plans are named without a path, and said to be deleted
what outlived each of them is named instead
- plan 1: the Form Row Contract of docs/TEMPLATES.md and getFieldIds()
- plan 2: the Settings Page Contract of the same file
- the two live defects plan 2 left open, and where each is now recorded
- the handover section below is flagged as the only surviving copy of what plan 2 hands over
Benefits:
- The reading order resolves; every docs path this plan names now exists
- A reader is not left guessing whether a missing prerequisite means missing work
Technical notes:
- Documentation only; no batch, no code and no behaviour is changed
The dead docs/FILE-MANAGER-CONCEPT-2026.md citation is left alone: the plan already knows it is dead and batch 3 is told to replace it