Files
markitect-main/capabilities/testdrive-jsui/node_modules/bidi-js/src/reordering.js
tegwick 17c62aadaa feat: complete testdrive-jsui capability extraction with full JavaScript test integration
Extract JavaScript UI framework functionality into dedicated testdrive-jsui capability
while maintaining 100% functionality preservation and integrating JavaScript tests
into the main Python test suite.

Phase 1 (Foundation Setup) - COMPLETED:
- Created capability directory structure with proper Python package layout
- Configured pyproject.toml with Node.js subprocess dependencies
- Set up package.json with Jest + JSDOM testing framework
- Implemented Python-JavaScript bridge for seamless test integration
- Created comprehensive capability Makefile with all testing targets
- Added detailed README documentation for capability usage

Phase 2 (Integration Layer) - COMPLETED:
- Built Python test wrappers for JavaScript test execution via subprocess
- Integrated with pytest discovery system for unified test experience
- Added capability targets to main Makefile delegation system
- Verified test integration works with main test suite

Phase 3 (Safe Migration) - COMPLETED:
- Copied (not moved) all JavaScript files to capability using safe copy-first approach
- Migrated 4 core JavaScript components and 11 test files (2,840+ lines)
- Verified all tests work in new location (11 Python tests + 7 JavaScript tests passing)
- Maintained dual-track testing capability for safety during transition

Phase 4 (Framework Enhancement) - COMPLETED:
- Enhanced testing framework with Python integration and coverage reporting
- Achieved 59% Python test coverage and 100% JavaScript test coverage
- Added performance benchmarking and component documentation

Phase 5 (Production Integration) - COMPLETED:
- Added standard 'test' target to capability Makefile for discovery system compatibility
- Integrated JavaScript tests into main Makefile with new targets:
  * test-js: Run JavaScript UI tests
  * test-all: Run all tests (Python + JavaScript + Capabilities)
- Updated help documentation to include new testing workflows
- Verified capability auto-discovery works via 'make test-capabilities'

Key Achievements:
- Zero-risk migration completed with copy-first safety approach
- Full Python-JavaScript test integration with 18 total passing tests
- JavaScript UI framework successfully extracted to dedicated capability
- Enhanced CI/CD integration with unified test command interface
- Clean architecture enabling future JavaScript framework evolution

Testing Status:
-  All Python integration tests passing (11/11)
-  All JavaScript component tests passing (7/7)
-  Capability discovery integration working
-  Main test suite integration complete
-  Test coverage reporting functional (59% Python, 100% JavaScript)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-09 22:29:30 +01:00

100 lines
3.8 KiB
JavaScript

import { getBidiCharType, TRAILING_TYPES } from './charTypes.js'
import { getMirroredCharacter } from './mirroring.js'
/**
* Given a start and end denoting a single line within a string, and a set of precalculated
* bidi embedding levels, produce a list of segments whose ordering should be flipped, in sequence.
* @param {string} string - the full input string
* @param {GetEmbeddingLevelsResult} embeddingLevelsResult - the result object from getEmbeddingLevels
* @param {number} [start] - first character in a subset of the full string
* @param {number} [end] - last character in a subset of the full string
* @return {number[][]} - the list of start/end segments that should be flipped, in order.
*/
export function getReorderSegments(string, embeddingLevelsResult, start, end) {
let strLen = string.length
start = Math.max(0, start == null ? 0 : +start)
end = Math.min(strLen - 1, end == null ? strLen - 1 : +end)
const segments = []
embeddingLevelsResult.paragraphs.forEach(paragraph => {
const lineStart = Math.max(start, paragraph.start)
const lineEnd = Math.min(end, paragraph.end)
if (lineStart < lineEnd) {
// Local slice for mutation
const lineLevels = embeddingLevelsResult.levels.slice(lineStart, lineEnd + 1)
// 3.4 L1.4: Reset any sequence of whitespace characters and/or isolate formatting characters at the
// end of the line to the paragraph level.
for (let i = lineEnd; i >= lineStart && (getBidiCharType(string[i]) & TRAILING_TYPES); i--) {
lineLevels[i] = paragraph.level
}
// L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels
// not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
let maxLevel = paragraph.level
let minOddLevel = Infinity
for (let i = 0; i < lineLevels.length; i++) {
const level = lineLevels[i]
if (level > maxLevel) maxLevel = level
if (level < minOddLevel) minOddLevel = level | 1
}
for (let lvl = maxLevel; lvl >= minOddLevel; lvl--) {
for (let i = 0; i < lineLevels.length; i++) {
if (lineLevels[i] >= lvl) {
const segStart = i
while (i + 1 < lineLevels.length && lineLevels[i + 1] >= lvl) {
i++
}
if (i > segStart) {
segments.push([segStart + lineStart, i + lineStart])
}
}
}
}
}
})
return segments
}
/**
* @param {string} string
* @param {GetEmbeddingLevelsResult} embedLevelsResult
* @param {number} [start]
* @param {number} [end]
* @return {string} the new string with bidi segments reordered
*/
export function getReorderedString(string, embedLevelsResult, start, end) {
const indices = getReorderedIndices(string, embedLevelsResult, start, end)
const chars = [...string]
indices.forEach((charIndex, i) => {
chars[i] = (
(embedLevelsResult.levels[charIndex] & 1) ? getMirroredCharacter(string[charIndex]) : null
) || string[charIndex]
})
return chars.join('')
}
/**
* @param {string} string
* @param {GetEmbeddingLevelsResult} embedLevelsResult
* @param {number} [start]
* @param {number} [end]
* @return {number[]} an array with character indices in their new bidi order
*/
export function getReorderedIndices(string, embedLevelsResult, start, end) {
const segments = getReorderSegments(string, embedLevelsResult, start, end)
// Fill an array with indices
const indices = []
for (let i = 0; i < string.length; i++) {
indices[i] = i
}
// Reverse each segment in order
segments.forEach(([start, end]) => {
const slice = indices.slice(start, end + 1)
for (let i = slice.length; i--;) {
indices[end - i] = slice[i]
}
})
return indices
}