UNPKG

shell-mirror

Version:

Access your Mac shell from any device securely. Perfect for mobile coding with Claude Code CLI, Gemini CLI, and any shell tool.

179 lines (159 loc) 5.65 kB
'use strict' var align = require('wide-align') var validate = require('aproba') var wideTruncate = require('./wide-truncate') var error = require('./error') var TemplateItem = require('./template-item') function renderValueWithValues (values) { return function (item) { return renderValue(item, values) } } var renderTemplate = module.exports = function (width, template, values) { var items = prepareItems(width, template, values) var rendered = items.map(renderValueWithValues(values)).join('') return align.left(wideTruncate(rendered, width), width) } function preType (item) { var cappedTypeName = item.type[0].toUpperCase() + item.type.slice(1) return 'pre' + cappedTypeName } function postType (item) { var cappedTypeName = item.type[0].toUpperCase() + item.type.slice(1) return 'post' + cappedTypeName } function hasPreOrPost (item, values) { if (!item.type) return return values[preType(item)] || values[postType(item)] } function generatePreAndPost (baseItem, parentValues) { var item = Object.assign({}, baseItem) var values = Object.create(parentValues) var template = [] var pre = preType(item) var post = postType(item) if (values[pre]) { template.push({value: values[pre]}) values[pre] = null } item.minLength = null item.length = null item.maxLength = null template.push(item) values[item.type] = values[item.type] if (values[post]) { template.push({value: values[post]}) values[post] = null } return function ($1, $2, length) { return renderTemplate(length, template, values) } } function prepareItems (width, template, values) { function cloneAndObjectify (item, index, arr) { var cloned = new TemplateItem(item, width) var type = cloned.type if (cloned.value == null) { if (!(type in values)) { if (cloned.default == null) { throw new error.MissingTemplateValue(cloned, values) } else { cloned.value = cloned.default } } else { cloned.value = values[type] } } if (cloned.value == null || cloned.value === '') return null cloned.index = index cloned.first = index === 0 cloned.last = index === arr.length - 1 if (hasPreOrPost(cloned, values)) cloned.value = generatePreAndPost(cloned, values) return cloned } var output = template.map(cloneAndObjectify).filter(function (item) { return item != null }) var remainingSpace = width var variableCount = output.length function consumeSpace (length) { if (length > remainingSpace) length = remainingSpace remainingSpace -= length } function finishSizing (item, length) { if (item.finished) throw new error.Internal('Tried to finish template item that was already finished') if (length === Infinity) throw new error.Internal('Length of template item cannot be infinity') if (length != null) item.length = length item.minLength = null item.maxLength = null --variableCount item.finished = true if (item.length == null) item.length = item.getBaseLength() if (item.length == null) throw new error.Internal('Finished template items must have a length') consumeSpace(item.getLength()) } output.forEach(function (item) { if (!item.kerning) return var prevPadRight = item.first ? 0 : output[item.index - 1].padRight if (!item.first && prevPadRight < item.kerning) item.padLeft = item.kerning - prevPadRight if (!item.last) item.padRight = item.kerning }) // Finish any that have a fixed (literal or intuited) length output.forEach(function (item) { if (item.getBaseLength() == null) return finishSizing(item) }) var resized = 0 var resizing var hunkSize do { resizing = false hunkSize = Math.round(remainingSpace / variableCount) output.forEach(function (item) { if (item.finished) return if (!item.maxLength) return if (item.getMaxLength() < hunkSize) { finishSizing(item, item.maxLength) resizing = true } }) } while (resizing && resized++ < output.length) if (resizing) throw new error.Internal('Resize loop iterated too many times while determining maxLength') resized = 0 do { resizing = false hunkSize = Math.round(remainingSpace / variableCount) output.forEach(function (item) { if (item.finished) return if (!item.minLength) return if (item.getMinLength() >= hunkSize) { finishSizing(item, item.minLength) resizing = true } }) } while (resizing && resized++ < output.length) if (resizing) throw new error.Internal('Resize loop iterated too many times while determining minLength') hunkSize = Math.round(remainingSpace / variableCount) output.forEach(function (item) { if (item.finished) return finishSizing(item, hunkSize) }) return output } function renderFunction (item, values, length) { validate('OON', arguments) if (item.type) { return item.value(values, values[item.type + 'Theme'] || {}, length) } else { return item.value(values, {}, length) } } function renderValue (item, values) { var length = item.getBaseLength() var value = typeof item.value === 'function' ? renderFunction(item, values, length) : item.value if (value == null || value === '') return '' var alignWith = align[item.align] || align.left var leftPadding = item.padLeft ? align.left('', item.padLeft) : '' var rightPadding = item.padRight ? align.right('', item.padRight) : '' var truncated = wideTruncate(String(value), length) var aligned = alignWith(truncated, length) return leftPadding + aligned + rightPadding }