<feed xmlns='http://www.w3.org/2005/Atom'>
<title>luci/modules/luci-base/htdocs, branch master</title>
<subtitle>Lua Configuration Interface (mirror)</subtitle>
<id>https://git-03.infra.openwrt.org/project/luci/atom?h=master</id>
<link rel='self' href='https://git-03.infra.openwrt.org/project/luci/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/'/>
<updated>2026-10-29T15:49:09Z</updated>
<entry>
<title>luci-base: add FileUploadList widget and form option</title>
<updated>2026-10-29T15:49:09Z</updated>
<author>
<name>Florian Eckert</name>
</author>
<published>2026-10-27T13:26:38Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=0e2783557642331eb0afdc92d02c1d624f44fef7'/>
<id>urn:sha1:0e2783557642331eb0afdc92d02c1d624f44fef7</id>
<content type='text'>
Add ui.FileUploadList and form.FileUploadList to select multiple files
below a root directory. Uploading and deleting files is optional
(enable_upload, enable_remove). Values are absolute paths like with
FileUpload unless absolute_path is disabled.

Signed-off-by: Florian Eckert &lt;fe@dev.tdt.de&gt;
</content>
</entry>
<entry>
<title>luci-base: ui: mark dropdown instead of hidden input in setInvalid()</title>
<updated>2026-10-29T15:48:47Z</updated>
<author>
<name>Florian Eckert</name>
</author>
<published>2026-10-22T10:27:07Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=22b00bafafcb2fbe30cd5fc8d4f2f01ec7837366'/>
<id>urn:sha1:22b00bafafcb2fbe30cd5fc8d4f2f01ec7837366</id>
<content type='text'>
If a widget is or contains a dropdown, 'setInvalid()' marked the input
of the dropdown, which is only visible while it is open. Mark the
dropdown itself instead.

Signed-off-by: Florian Eckert &lt;fe@dev.tdt.de&gt;
</content>
</entry>
<entry>
<title>luci-base: ui: do not open an empty Dropdown</title>
<updated>2026-10-23T07:33:38Z</updated>
<author>
<name>Florian Eckert</name>
</author>
<published>2026-10-20T14:20:57Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=1c67452adb01413e78feb109180bd15186612ead'/>
<id>urn:sha1:1c67452adb01413e78feb109180bd15186612ead</id>
<content type='text'>
If a Dropdown has no choices and no create input, e.g. a form.MultiValue
whose values are added dynamically in load() and there is nothing to
select yet, its list contains no items.

Opening such a dropdown still registers a transitionend handler that
calls setFocus() with 'sel ?? li[0]', which is undefined in this case.
As the dropdown list has a max-height transition, the handler fires and
fails with:

  TypeError: can't access property "classList", elem is undefined

Besides that, an empty dropdown list is opened with nothing to show or
select.

Do not open the dropdown at all if there are no items.

Signed-off-by: Florian Eckert &lt;fe@dev.tdt.de&gt;
</content>
</entry>
<entry>
<title>luci-base: ui: handle null choices in Dropdown</title>
<updated>2026-10-23T07:33:38Z</updated>
<author>
<name>Florian Eckert</name>
</author>
<published>2026-10-20T14:20:33Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=7bfac279bb3cff62aa9bb24e7cc112d752e87ab0'/>
<id>urn:sha1:7bfac279bb3cff62aa9bb24e7cc112d752e87ab0</id>
<content type='text'>
form.AbstractValue.transformChoices() returns null if no choices were
added to the option, e.g. if a form.MultiValue is filled dynamically in
load() and there is nothing to select yet.

ui.Dropdown is supposed to fall back to an empty choice object in this
case, but the check typeof(choices) != 'object' does not catch null,
as typeof(null) is 'object'. Rendering then fails in render() with:

  TypeError: can't convert null to object

Use L.isObject() instead, like ui.Select already does, so that null is
replaced by an empty object as well.

Signed-off-by: Florian Eckert &lt;fe@dev.tdt.de&gt;
</content>
</entry>
<entry>
<title>luci-base: mark empty required fields as invalid on save</title>
<updated>2026-10-21T07:09:50Z</updated>
<author>
<name>Florian Eckert</name>
</author>
<published>2026-10-19T10:28:41Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=1dce3af9f3b676f9d6a3c7b13c1fa596fe9572b6'/>
<id>urn:sha1:1dce3af9f3b676f9d6a3c7b13c1fa596fe9572b6</id>
<content type='text'>
A required option 'o.rmempty = false' that is empty is rejected in
'AbstractValue.parse()' function with 'must not be empty', but nothing
is marked in the form. This is most visible in modal dialogs, which save
silently: the save does nothing and the user gets no hint what is
missing.

LuCI only marks fields through the input validator. DynamicList forces
optional: true, so an empty list is never flagged, and a Value without
datatype or validate has no validator at all.

To fix this add 'ui.AbstractElement.setInvalid()', which sets
'cbi-input-invalid' and an error tooltip on the visible input and
clears it again on the next edit. Call it from 'AbstractValue.parse()'
and refresh the tab error counts with 'ui.tabs.updateTabs()', which is
otherwise not run on save.

Signed-off-by: Florian Eckert &lt;fe@dev.tdt.de&gt;
</content>
</entry>
<entry>
<title>luci-base: form: move tooltip option from Flag to Value</title>
<updated>2026-10-12T11:11:46Z</updated>
<author>
<name>Florian Eckert</name>
</author>
<published>2026-10-09T08:20:05Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=d12d55376188343d790fa40377c084245a4d2b86'/>
<id>urn:sha1:d12d55376188343d790fa40377c084245a4d2b86</id>
<content type='text'>
Until now only form.Flag supported the tooltip and tooltipicon
options, and the tooltip was rendered inside the ui.Checkbox widget.

Move both options to form.Value so that they are available for all
option types, e.g. Value, ListValue, DynamicList or TextValue. The
tooltip is rendered by the new renderTooltip() and added by
renderFrame(). form.Flag inherits it and no longer passes the options
to ui.Checkbox.

The tooltip can be a string (formatted with the section ID), a
function returning a string, or an array. The items of an array are
separated by line breaks, which allows multi-line tooltips without
embedding HTML.

To always show the tooltip icon behind the widget, independent of
whether the widget frame is a block or an inline element (e.g. text
inputs, textareas and selects use plain block frames), the widget and
the icon are wrapped into an inline-flex container if a tooltip is set.
Options without a tooltip are not changed.

Co-Authored-By: Claude Sonnet 5.5 &lt;noreply@anthropic.com&gt;
Signed-off-by: Florian Eckert &lt;fe@dev.tdt.de&gt;
</content>
</entry>
<entry>
<title>luci-base: fix JSDoc of option(), taboption() and addNotification()</title>
<updated>2026-10-07T17:41:22Z</updated>
<author>
<name>Victor Irzak</name>
</author>
<published>2026-10-03T01:46:19Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=7fccab79e32cfd0907f5562416983cf9448b33e0'/>
<id>urn:sha1:7fccab79e32cfd0907f5562416983cf9448b33e0</id>
<content type='text'>
form: option() documented three parameters for its two: the class as
`optionclass` and the real `cbiClass` as `{object}` classargs. Document
`cbiClass` and `args`. taboption() documented an `optionclass` its
signature (tabName, ...args) does not have; name the class parameter
in the signature instead, which behaves the same. Both said the class
must derive from AbstractSection where they check for AbstractValue.

ui: addNotification() describes a `null` title and is called with one
throughout LuCI, but typed it as string; mark it nullable.

luci: XMLHTTPRequest is spelled XMLHttpRequest.
Signed-off-by: Victor Irzak &lt;victor@irzak.com&gt;
</content>
</entry>
<entry>
<title>luci-base: form: don't let JSONMap add() reuse an existing section</title>
<updated>2026-10-07T17:38:49Z</updated>
<author>
<name>Victor Irzak</name>
</author>
<published>2026-10-02T21:38:32Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=a92bec6a9b502b5e737cbd9179594ef1cbe59b2d'/>
<id>urn:sha1:a92bec6a9b502b5e737cbd9179594ef1cbe59b2d</id>
<content type='text'>
CBIJSONConfig names anonymous sections loaded from an array
&lt;type&gt;&lt;n&gt;, with n counted across all section types, while add()
names a new one &lt;type&gt;&lt;count of that type&gt;. With data like
{ animals: {...}, pet: [ {...} ] } the loaded pet is "pet1" and
add() also returns "pet1", so the new entry overwrites the existing
one instead of being appended. The same happens after a remove().

Pick the first unused name instead.

Signed-off-by: Victor Irzak &lt;victor@irzak.com&gt;
</content>
</entry>
<entry>
<title>treewide: wrap loose view content in cbi-section</title>
<updated>2026-09-06T18:47:04Z</updated>
<author>
<name>Ivan Kvashonkin</name>
</author>
<published>2026-09-05T16:43:28Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=9aff4652380831e40b075a6f698ca427ee59fd04'/>
<id>urn:sha1:9aff4652380831e40b075a6f698ca427ee59fd04</id>
<content type='text'>
Views that returned headings, tables and control rows with nothing around
them now wrap that content in a section: the system and kernel logs,
Processes, the nftables and iptables views, Startup, the wireless station
list, DSL stats and the package manager. The nftables rule tables also
drop an !important that no theme could override.

Nothing changes in the stock bootstrap theme; themes that draw
.cbi-section as a panel get the structure they need.

Signed-off-by: Ivan Kvashonkin &lt;vizzlef@gmail.com&gt;
</content>
</entry>
<entry>
<title>treewide: fix textarea double scrolling issue</title>
<updated>2026-09-05T15:16:34Z</updated>
<author>
<name>Michael Pratt</name>
</author>
<published>2026-08-22T17:42:26Z</published>
<link rel='alternate' type='text/html' href='https://git-03.infra.openwrt.org/project/luci/commit/?id=82966a62d0adb6ca0785f296f1d36e72cb9f7a3a'/>
<id>urn:sha1:82966a62d0adb6ca0785f296f1d36e72cb9f7a3a</id>
<content type='text'>
In "textarea" class elements, like the syslog,
depending on the browser, it is difficult to scroll if moused over
or after clicking or highlighting text.

The browser perceives that there is scrolling space/content within
the inner element because when the height of the element is exactly
the same size as text inside, the text padding or other invisible content
exceeds the size of the text box and the scroll bar is kept in the window.

This causes scrolling within the element with a range of just a few pixels,
and the rest of the page remains static and refuses to scroll.

To fix this add 1 to the dynamic size of the "textarea" boxes,
and also the initial values before the dynamic size is calculated.
This "+ 1" already exists in some instances of similar Javascript files.

A previous attempt at fixing this used "overflow-y" styling
but that causes undesirable behavior in some "textarea" use cases.

In some cases "+ 1" is added to both dynamic and final sizes, this can equate
to a total addition of 2 lines, which is still reasonable and proper padding,
and this promotes the habit of padding the textarea elements treewide,
no matter what example is used to modify or create the javascript for a new app.

The aria2 application ternary check for a minimum textarea size
seems to be written in reversed logic. It should likely be a minimum of 20 lines,
and the exact number + 1 otherwise, so fix that while at it.

Tested on Chrome 64-bit Windows 10.

Ref: 755061bc9e7e ("themes: remove overflow-y property...")
Signed-off-by: Michael Pratt &lt;mcpratt@pm.me&gt;
</content>
</entry>
</feed>
