Skip to content

Commit 7438a5b

Browse files
committed
removes SOS/EOS states
- updates docs, code and tests SOS and EOS were automatically added to the depth 0 stepper node as a way to know when you get to start or end. it ended up not being useful to end user, functionality could be delievered without it, and complicated many tests/code.
1 parent 932ba02 commit 7438a5b

15 files changed

Lines changed: 221 additions & 365 deletions

File tree

docs/api.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ The following functions are available when using `useViewAPI`:
4949
- `stepIndex`: Returns the current step index.
5050
- `paths`: Returns the current paths.
5151
- `path`: Returns the current path.
52-
- `length`: Returns the number of steps (excluding SOS/EOS).
52+
- `length`: Returns the number of steps
5353
- `nSteps`: Alias for `length`.
54-
- `nrows`: Returns the total number of rows including SOS/EOS.
54+
- `nrows`: Returns the total number of rows.
5555

5656
### Table Management
5757

@@ -200,8 +200,6 @@ stepper.shuffle({ always: true }) // Always shuffles without a seed
200200
- The shuffle operation is tracked internally. Once shuffled, subsequent calls
201201
to shuffle will be ignored unless `always` is set to true.
202202
- If there is only one or zero states, the shuffle operation is skipped.
203-
- At depth 0 (root level), the shuffle operation preserves the SOS and EOS
204-
states at the beginning and end of the sequence.
205203

206204
## Logging and Debugging
207205

src/builtins/captcha/CaptchaView.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ function next_trial() {
8888

8989
<template>
9090
<div class="page">
91-
<div class="instructions" v-if="api.paths == 'EOS'">
91+
<div class="instructions" v-if="api.stepIndex >= api.nSteps">
9292
<div class="formstep">
9393
<article class="message is-danger">
9494
<div class="message-header">

src/builtins/tasks/Task2View.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const trials = api.steps.append([
3434
// set up the call backs that take you through the task
3535
3636
function next() {
37-
if (api.stepIndex < api.nSteps) {
37+
if (api.stepIndex < api.nSteps - 1) {
3838
api.goNextStep()
3939
} else {
4040
api.goNextView()

src/core/composables/useStepper.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99
import Stepper from '@/core/stepper/Stepper'
1010
import useSmileStore from '@/core/stores/smilestore'
11-
11+
import useLog from '@/core/stores/log'
1212
/**
1313
* @class StepperAPI
1414
* @description Wraps a Stepper instance to provide a consistent API for managing multi-step processes
@@ -29,6 +29,7 @@ import useSmileStore from '@/core/stores/smilestore'
2929
*/
3030
export function useStepper(view) {
3131
const smilestore = useSmileStore()
32+
const log = useLog()
3233

3334
// Set viewProvidesStepper to true for the dev bar
3435
smilestore.dev.viewProvidesStepper = true
@@ -41,19 +42,19 @@ export function useStepper(view) {
4142
const savedState = smilestore.getStepper(view)?.data
4243
if (savedState) {
4344
try {
44-
console.log('STEPPER: Loading saved state from smilestore for view', view)
45+
//console.log('STEPPER: Loading saved state from smilestore for view', view)
4546
stepper = new Stepper({ serializedState: savedState.stepperState, store: smilestore })
4647
} catch (error) {
47-
console.error('STEPPER: Failed to load saved state, creating new stepper:', error.message)
48+
log.error('STEPPER: Failed to load saved state, creating new stepper:', error.message)
4849
stepper = new Stepper({ id: '/', parent: null, data: { gvars: {} }, store: smilestore })
4950
}
5051
} else {
51-
console.log('STEPPER: Initializing state machine with SOS and EOS nodes for view', view)
52+
//console.log('STEPPER: Initializing state machine for view', view)
5253
stepper = new Stepper({ id: '/', parent: null, data: { gvars: {} }, store: smilestore })
5354
}
5455
stepper.name = view
5556
// Register stepper if not already registered
56-
console.log('STEPPER: Registering stepper for view', stepper.name)
57+
//console.log('STEPPER: Registering stepper for view', stepper.name)
5758
if (view) {
5859
stepper = smilestore.registerStepper(view, stepper)
5960
}

src/core/composables/useViewAPI.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,15 @@ class ViewAPI extends SmileAPI {
274274
}
275275

276276
get length() {
277-
return this._stepper.value.countLeafNodes - 2
277+
return this._stepper.value.countLeafNodes
278278
}
279279

280280
get nSteps() {
281-
return this._stepper.value.countLeafNodes - 2
281+
return this._stepper.value.countLeafNodes
282282
}
283283

284284
get nrows() {
285-
return this._stepper.value.countLeafNodes
285+
return this.nSteps
286286
}
287287

288288
// Getter for persisted variable that provides access to gvars from stepper root data
@@ -354,9 +354,6 @@ class ViewAPI extends SmileAPI {
354354

355355
const getLeafData = (state) => {
356356
if (state.isLeaf) {
357-
if (state.id === 'SOS' || state.id === 'EOS') {
358-
return []
359-
}
360357
if (matchesFilter(state.pathString, pathFilter)) {
361358
return state.data
362359
}
@@ -375,9 +372,6 @@ class ViewAPI extends SmileAPI {
375372
this.store.browserPersisted.viewSteppers[this._page.value].data = pageData
376373

377374
this._stepper.value.clearSubTree()
378-
this._stepper.value.push('SOS')
379-
this._stepper.value.push('EOS')
380-
this._stepper.value._currentIndex = 1
381375

382376
// this._path.value = []
383377
// this._pathString.value = ''

src/core/stepper/StepState.js

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export class StepState {
201201
*
202202
* A leaf node is considered "first" if:
203203
* 1. It is a leaf node (has no children)
204-
* 2. It is the leftmost leaf node when traversing the tree that is not marked as 'SOS' or 'EOS'
204+
* 2. It is the leftmost leaf node when traversing the tree
205205
*
206206
* @returns {boolean} True if this is the first non-special leaf node, false otherwise
207207
*/
@@ -219,13 +219,11 @@ export class StepState {
219219
return findLeftmostLeaf(state._states[0])
220220
}
221221

222-
// Find first non-SOS leaf by checking each branch from left
222+
// Find first leaf by checking each branch from left
223223
for (let i = 0; i < this._root._states.length; i++) {
224224
const leftmostLeaf = findLeftmostLeaf(this._root._states[i])
225-
if (leftmostLeaf.id !== 'SOS' && leftmostLeaf.id !== 'EOS') {
226-
// Return true if this is that first non-SOS leaf
227-
return leftmostLeaf.pathString === this.pathString
228-
}
225+
// Return true if this is that first leaf
226+
return leftmostLeaf.pathString === this.pathString
229227
}
230228

231229
return false
@@ -357,11 +355,7 @@ export class StepState {
357355
* then recursively resets all child nodes.
358356
*/
359357
reset() {
360-
if (this._depth === 0) {
361-
this._currentIndex = 1
362-
} else {
363-
this._currentIndex = 0
364-
}
358+
this._currentIndex = 0
365359
this._states.forEach((state) => state.reset())
366360
}
367361

src/core/stepper/Stepper.js

Lines changed: 11 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,6 @@ export class Stepper extends StepState {
5353
if (serializedState !== null) {
5454
this._log.debug('STEPSTATE:loading serialized state')
5555
this.loadFromJSON(serializedState)
56-
} else {
57-
// Initialize the state machine with SOS and EOS nodes
58-
if (this.depth === 0) {
59-
this.push('SOS')
60-
this.push('EOS')
61-
this._currentIndex = 1 // start at EOS or whatever is first
62-
}
6356
}
6457

6558
return new StepperProxy(this)
@@ -144,19 +137,9 @@ export class Stepper extends StepState {
144137
try {
145138
// Create a new state with auto-incremented id
146139
if (item.path !== undefined) {
147-
// If at level 0, insert before EOS state
148-
if (this.depth === 0) {
149-
state = this.insert(item.path, -2, item)
150-
} else {
151-
state = this.push(item.path, item)
152-
}
140+
state = this.push(item.path, item)
153141
} else {
154-
// If at level 0, insert before EOS state
155-
if (this.depth === 0) {
156-
state = this.insert(null, -2, item)
157-
} else {
158-
state = this.push(null, item)
159-
}
142+
state = this.push(null, item)
160143
}
161144
} catch (error) {
162145
this._log.error(error.message)
@@ -176,8 +159,7 @@ export class Stepper extends StepState {
176159
const itemsToAdd = Array.isArray(items) ? items : [items]
177160

178161
// Check if adding these items would exceed maxStepperRows
179-
// Subtract 2 to account for SOS and EOS states
180-
if (this._states.length - 2 + itemsToAdd.length > config.maxStepperRows) {
162+
if (this._states.length + itemsToAdd.length > config.maxStepperRows) {
181163
this._log.error(
182164
`Cannot append ${itemsToAdd.length} rows as it exceeds the safety limit of ${config.maxStepperRows}`
183165
)
@@ -402,32 +384,14 @@ export class Stepper extends StepState {
402384
rng = Math.random
403385
}
404386

405-
// Fisher-Yates shuffle algorithm
406-
if (this.depth === 0) {
407-
// At depth 0, we need to preserve SOS and EOS states
408-
// Only shuffle the middle elements (excluding first and last)
409-
const start = 1 // Skip SOS
410-
const end = this._states.length - 1 // Skip EOS
411-
412-
for (let i = end - 1; i > start; i--) {
413-
// j can be between start and i (excluding SOS and EOS)
414-
const j = start + Math.floor(rng() * (i - start + 1))
415-
;[this._states[i], this._states[j]] = [this._states[j], this._states[i]]
416-
417-
// Update indices for the swapped elements
418-
this._states[i]._currentIndex = i - 1 // -1 because we skip SOS
419-
this._states[j]._currentIndex = j - 1 // -1 because we skip SOS
420-
}
421-
} else {
422-
// For other depths, shuffle all elements
423-
for (let i = this._states.length - 1; i > 0; i--) {
424-
const j = Math.floor(rng() * (i + 1))
425-
;[this._states[i], this._states[j]] = [this._states[j], this._states[i]]
426-
427-
// Update indices for the swapped elements
428-
this._states[i]._currentIndex = i
429-
this._states[j]._currentIndex = j
430-
}
387+
// For other depths, shuffle all elements
388+
for (let i = this._states.length - 1; i > 0; i--) {
389+
const j = Math.floor(rng() * (i + 1))
390+
;[this._states[i], this._states[j]] = [this._states[j], this._states[i]]
391+
392+
// Update indices for the swapped elements
393+
this._states[i]._currentIndex = i
394+
this._states[j]._currentIndex = j
431395
}
432396

433397
// Mark as shuffled
@@ -447,11 +411,6 @@ export class Stepper extends StepState {
447411
*/
448412
forEach(callback) {
449413
this._states.forEach((item, index) => {
450-
// Skip SOS and EOS states when at depth 0
451-
if (this.depth === 0 && (index === 0 || index === this.states.length - 1)) {
452-
return
453-
}
454-
455414
// Call the callback and check if it returns a new value
456415
const result = callback(item, index)
457416
if (result !== undefined) {
@@ -585,12 +544,6 @@ export class Stepper extends StepState {
585544
*/
586545
clearSubTree() {
587546
super.clearSubTree()
588-
// Initialize the state machine with SOS and EOS nodes
589-
if (this.depth === 0) {
590-
this.push('SOS')
591-
this.push('EOS')
592-
this._currentIndex = 1 // start at EOS or whatever is first
593-
}
594547

595548
this._shuffled = false
596549
}

src/core/stepper/StepperProxy.js

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @class StepperProxy
3-
* @description Proxy handler for TableAPI objects that handles array-like access and SOS/EOS states
3+
* @description Proxy handler for TableAPI objects that handles array-like access
44
*/
55
class StepperProxy {
66
/**
@@ -20,7 +20,7 @@ class StepperProxy {
2020
* @returns {*} The value of the property
2121
*/
2222
get(target, prop) {
23-
console.log('get', prop)
23+
//console.log('get', prop)
2424
// Handle array/object access
2525
if (typeof prop === 'string' || typeof prop === 'number') {
2626
// Convert string numbers to actual numbers
@@ -35,9 +35,6 @@ class StepperProxy {
3535
// If still negative after adjustment, return undefined
3636
if (prop < 0) return undefined
3737
}
38-
39-
// Then adjust for SOS/EOS states at the root level
40-
prop = this.adjustIndex(prop, target)
4138
}
4239

4340
// IMPORTANT: We first try to get a child node by id before checking properties/methods
@@ -55,26 +52,6 @@ class StepperProxy {
5552
}
5653
return target[prop]
5754
}
58-
59-
/**
60-
* Adjusts the index to account for SOS/EOS states at the root level
61-
* @param {number} index - The index to adjust
62-
* @param {StepState} node - The node being accessed
63-
* @returns {number} The adjusted index
64-
*/
65-
adjustIndex(index, node) {
66-
// If this is the root node (depth 0), adjust indices to skip SOS/EOS states
67-
if (node.depth === 0) {
68-
if (index >= 0) {
69-
// Add 1 to skip SOS state
70-
return index + 1
71-
} else {
72-
// Subtract 1 to skip EOS state
73-
return index - 1
74-
}
75-
}
76-
return index
77-
}
7855
}
7956

8057
export default StepperProxy

src/dev/developer_mode/StateTreeViewer.vue

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,6 @@ const handleReload = () => {
111111
window.location.reload()
112112
}
113113
114-
const isEndState = computed(() => {
115-
if (!api.steps) return false
116-
return api.pathString === 'SOS' || api.pathString === 'EOS'
117-
})
118114
119115
// Add refs for container and selected node tracking
120116
const treeContainer = ref(null)
@@ -167,7 +163,7 @@ watch(
167163
<div class="path-info">
168164
<div class="path-display">{{ api.pathString }}</div>
169165

170-
&nbsp;<FAIcon icon="fa-solid fa-ban" v-if="isEndState" />
166+
171167
</div>
172168

173169
<div class="field has-addons">

src/dev/developer_mode/TreeNode.vue

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ const getChildVerticalLines = (verticalLines, index, total) => {
5454
5555
return newLines
5656
}
57-
58-
const isEndState = computed(() => {
59-
return props.state.path === 'SOS' || props.state.path === 'EOS'
60-
})
6157
</script>
6258

6359
<template>
@@ -70,12 +66,11 @@ const isEndState = computed(() => {
7066
@click="handleClick"
7167
>
7268
<span class="vertical-lines">{{ getVerticalPrefix(verticalLines) }}</span>
73-
<span class="tree-branch" :class="{ 'end-state': isEndState }">{{ getBranchType(index, total, depth) }}</span>
74-
<span class="node-path" :class="{ 'end-state': isEndState, 'leaf-state': !state.isLeaf }"
69+
<span class="tree-branch">{{ getBranchType(index, total, depth) }}</span>
70+
<span class="node-path" :class="{ 'leaf-state': !state.isLeaf }"
7571
>{{ state.path }} <FAIcon icon="fa-solid fa-house-flag" class="home-icon" v-if="state.isFirstLeaf" />
76-
<FAIcon icon="fa-solid fa-leaf" class="leaf-icon" v-else-if="state.isLeaf && !isEndState" />
77-
<FAIcon icon="fa-solid fa-ban" v-if="isEndState"
78-
/></span>
72+
<FAIcon icon="fa-solid fa-leaf" class="leaf-icon" v-else-if="state.isLeaf" />
73+
</span>
7974
</div>
8075

8176
<!-- Recursive tree nodes, but limit depth -->
@@ -123,6 +118,7 @@ const isEndState = computed(() => {
123118
background-color: rgb(146, 249, 224);
124119
color: black;
125120
border-radius: 10px;
121+
margin-right: 35px;
126122
}
127123
128124
.vertical-lines {

0 commit comments

Comments
 (0)