mirror of
https://github.com/taigrr/teaqlite.git
synced 2026-04-02 04:59:03 -07:00
adding exemptions for sql
This commit is contained in:
20
README.md
20
README.md
@@ -64,12 +64,13 @@ go run main.go sample.db
|
|||||||
|
|
||||||
### Cell Edit Mode
|
### Cell Edit Mode
|
||||||
- Type new value for the selected cell
|
- Type new value for the selected cell
|
||||||
|
- **Text Wrapping**: Long values are automatically wrapped for better visibility
|
||||||
- `Enter`: Save changes to database
|
- `Enter`: Save changes to database
|
||||||
- `Esc`: Cancel editing and return to row detail
|
- `Esc`: Cancel editing and return to row detail
|
||||||
- `Backspace`: Delete characters
|
- `Backspace`: Delete characters
|
||||||
|
|
||||||
### SQL Query Mode
|
### SQL Query Mode
|
||||||
- Type your SQL query
|
- Type your SQL query (all keys including r, s, h, j, k, l work as input)
|
||||||
- `Enter`: Execute query
|
- `Enter`: Execute query
|
||||||
- `Backspace`: Delete characters
|
- `Backspace`: Delete characters
|
||||||
- `Esc`: Return to table list
|
- `Esc`: Return to table list
|
||||||
@@ -84,14 +85,15 @@ go run main.go sample.db
|
|||||||
5. **Data Search**: Search within table data across all columns
|
5. **Data Search**: Search within table data across all columns
|
||||||
6. **Row Detail Modal**: 2-column view showing Column | Value for selected row
|
6. **Row Detail Modal**: 2-column view showing Column | Value for selected row
|
||||||
7. **Cell Editing**: Live editing of individual cell values with database updates
|
7. **Cell Editing**: Live editing of individual cell values with database updates
|
||||||
8. **Primary Key Detection**: Uses primary keys for reliable row updates
|
8. **Text Wrapping**: Long values are automatically wrapped in edit and detail views
|
||||||
9. **Screen-Aware Display**: Content automatically fits terminal size
|
9. **Primary Key Detection**: Uses primary keys for reliable row updates
|
||||||
10. **SQL Query Execution**: Execute custom SQL queries and view results
|
10. **Screen-Aware Display**: Content automatically fits terminal size
|
||||||
11. **Error Handling**: Displays database errors gracefully
|
11. **SQL Query Execution**: Execute custom SQL queries and view results (all keys work as input)
|
||||||
12. **Responsive UI**: Clean, styled interface that adapts to terminal size
|
12. **Error Handling**: Displays database errors gracefully
|
||||||
13. **Column Information**: Shows column names and handles NULL values
|
13. **Responsive UI**: Clean, styled interface that adapts to terminal size
|
||||||
14. **Navigation**: Intuitive keyboard shortcuts for all operations
|
14. **Column Information**: Shows column names and handles NULL values
|
||||||
15. **Dynamic Column Width**: Columns adjust to terminal width
|
15. **Navigation**: Intuitive keyboard shortcuts for all operations
|
||||||
|
16. **Dynamic Column Width**: Columns adjust to terminal width
|
||||||
|
|
||||||
## Navigation Flow
|
## Navigation Flow
|
||||||
|
|
||||||
|
|||||||
137
main.go
137
main.go
@@ -546,6 +546,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if m.selectedCol > 0 {
|
if m.selectedCol > 0 {
|
||||||
m.selectedCol--
|
m.selectedCol--
|
||||||
}
|
}
|
||||||
|
case modeQuery:
|
||||||
|
// In query mode, these should be treated as input
|
||||||
|
if len(msg.String()) == 1 {
|
||||||
|
m.queryInput += msg.String()
|
||||||
|
m.query = m.queryInput
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "down", "j":
|
case "down", "j":
|
||||||
@@ -567,6 +573,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if m.selectedCol < len(m.columns)-1 {
|
if m.selectedCol < len(m.columns)-1 {
|
||||||
m.selectedCol++
|
m.selectedCol++
|
||||||
}
|
}
|
||||||
|
case modeQuery:
|
||||||
|
// In query mode, these should be treated as input
|
||||||
|
if len(msg.String()) == 1 {
|
||||||
|
m.queryInput += msg.String()
|
||||||
|
m.query = m.queryInput
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "left", "h":
|
case "left", "h":
|
||||||
@@ -584,6 +596,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
visibleCount := m.getVisibleTableCount()
|
visibleCount := m.getVisibleTableCount()
|
||||||
m.selectedTable = m.tableListPage * visibleCount
|
m.selectedTable = m.tableListPage * visibleCount
|
||||||
}
|
}
|
||||||
|
case modeQuery:
|
||||||
|
// In query mode, these should be treated as input
|
||||||
|
if len(msg.String()) == 1 {
|
||||||
|
m.queryInput += msg.String()
|
||||||
|
m.query = m.queryInput
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "right", "l":
|
case "right", "l":
|
||||||
@@ -606,6 +624,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.selectedTable = len(m.filteredTables) - 1
|
m.selectedTable = len(m.filteredTables) - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case modeQuery:
|
||||||
|
// In query mode, these should be treated as input
|
||||||
|
if len(msg.String()) == 1 {
|
||||||
|
m.queryInput += msg.String()
|
||||||
|
m.query = m.queryInput
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "s":
|
case "s":
|
||||||
@@ -614,6 +638,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.queryInput = ""
|
m.queryInput = ""
|
||||||
m.query = ""
|
m.query = ""
|
||||||
m.cursor = 0
|
m.cursor = 0
|
||||||
|
} else if m.mode == modeQuery {
|
||||||
|
// In query mode, 's' should be treated as input
|
||||||
|
m.queryInput += msg.String()
|
||||||
|
m.query = m.queryInput
|
||||||
}
|
}
|
||||||
|
|
||||||
case "r":
|
case "r":
|
||||||
@@ -624,6 +652,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.loadTableData()
|
m.loadTableData()
|
||||||
case modeRowDetail:
|
case modeRowDetail:
|
||||||
m.loadTableData()
|
m.loadTableData()
|
||||||
|
case modeQuery:
|
||||||
|
// In query mode, 'r' should be treated as input
|
||||||
|
m.queryInput += msg.String()
|
||||||
|
m.query = m.queryInput
|
||||||
}
|
}
|
||||||
|
|
||||||
case "backspace":
|
case "backspace":
|
||||||
@@ -824,10 +856,43 @@ func (m model) View() string {
|
|||||||
col := m.columns[i]
|
col := m.columns[i]
|
||||||
val := row[i]
|
val := row[i]
|
||||||
|
|
||||||
dataRow := fmt.Sprintf("%-*s | %-*s",
|
// For long values, show them wrapped on multiple lines
|
||||||
|
if len(val) > valueWidth {
|
||||||
|
// First line with column name
|
||||||
|
firstLine := fmt.Sprintf("%-*s | %-*s",
|
||||||
colWidth, truncateString(col, colWidth),
|
colWidth, truncateString(col, colWidth),
|
||||||
valueWidth, truncateString(val, valueWidth))
|
valueWidth, truncateString(val, valueWidth))
|
||||||
|
|
||||||
|
if i == m.selectedCol {
|
||||||
|
content.WriteString(selectedStyle.Render(firstLine))
|
||||||
|
} else {
|
||||||
|
content.WriteString(normalStyle.Render(firstLine))
|
||||||
|
}
|
||||||
|
content.WriteString("\n")
|
||||||
|
|
||||||
|
// Additional lines for wrapped text (if there's space)
|
||||||
|
if len(val) > valueWidth && visibleRows > displayRows {
|
||||||
|
wrappedLines := wrapText(val, valueWidth)
|
||||||
|
for j, wrappedLine := range wrappedLines[1:] { // Skip first line already shown
|
||||||
|
if j >= 2 { // Limit to 3 total lines per field
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continuationLine := fmt.Sprintf("%-*s | %-*s",
|
||||||
|
colWidth, "", valueWidth, wrappedLine)
|
||||||
|
if i == m.selectedCol {
|
||||||
|
content.WriteString(selectedStyle.Render(continuationLine))
|
||||||
|
} else {
|
||||||
|
content.WriteString(normalStyle.Render(continuationLine))
|
||||||
|
}
|
||||||
|
content.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Normal single line
|
||||||
|
dataRow := fmt.Sprintf("%-*s | %-*s",
|
||||||
|
colWidth, truncateString(col, colWidth),
|
||||||
|
valueWidth, val)
|
||||||
|
|
||||||
if i == m.selectedCol {
|
if i == m.selectedCol {
|
||||||
content.WriteString(selectedStyle.Render(dataRow))
|
content.WriteString(selectedStyle.Render(dataRow))
|
||||||
} else {
|
} else {
|
||||||
@@ -836,6 +901,7 @@ func (m model) View() string {
|
|||||||
content.WriteString("\n")
|
content.WriteString("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
content.WriteString("\n")
|
content.WriteString("\n")
|
||||||
content.WriteString(helpStyle.Render("↑/↓: select field • enter: edit • esc: back • q: quit"))
|
content.WriteString(helpStyle.Render("↑/↓: select field • enter: edit • esc: back • q: quit"))
|
||||||
@@ -850,11 +916,30 @@ func (m model) View() string {
|
|||||||
content.WriteString(titleStyle.Render(fmt.Sprintf("Edit: %s.%s", tableName, columnName)))
|
content.WriteString(titleStyle.Render(fmt.Sprintf("Edit: %s.%s", tableName, columnName)))
|
||||||
content.WriteString("\n\n")
|
content.WriteString("\n\n")
|
||||||
|
|
||||||
content.WriteString("Original: " + m.originalValue)
|
// Calculate available width for text (leave some margin)
|
||||||
content.WriteString("\n")
|
textWidth := max(20, m.width-4)
|
||||||
content.WriteString("New: " + m.editingValue + "_")
|
|
||||||
content.WriteString("\n\n")
|
|
||||||
|
|
||||||
|
// Wrap original value
|
||||||
|
content.WriteString("Original:")
|
||||||
|
content.WriteString("\n")
|
||||||
|
originalLines := wrapText(m.originalValue, textWidth)
|
||||||
|
for _, line := range originalLines {
|
||||||
|
content.WriteString(" " + line)
|
||||||
|
content.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
content.WriteString("\n")
|
||||||
|
|
||||||
|
// Wrap new value
|
||||||
|
content.WriteString("New:")
|
||||||
|
content.WriteString("\n")
|
||||||
|
newLines := wrapText(m.editingValue+"_", textWidth) // Add cursor
|
||||||
|
for _, line := range newLines {
|
||||||
|
content.WriteString(" " + line)
|
||||||
|
content.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
content.WriteString("\n")
|
||||||
content.WriteString(helpStyle.Render("Type new value • enter: save • esc: cancel"))
|
content.WriteString(helpStyle.Render("Type new value • enter: save • esc: cancel"))
|
||||||
|
|
||||||
case modeQuery:
|
case modeQuery:
|
||||||
@@ -938,6 +1023,48 @@ func truncateString(s string, maxLen int) string {
|
|||||||
return s[:maxLen-3] + "..."
|
return s[:maxLen-3] + "..."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapText(text string, width int) []string {
|
||||||
|
if width <= 0 {
|
||||||
|
return []string{text}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lines []string
|
||||||
|
words := strings.Fields(text)
|
||||||
|
if len(words) == 0 {
|
||||||
|
return []string{text}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentLine := ""
|
||||||
|
for _, word := range words {
|
||||||
|
// If adding this word would exceed the width
|
||||||
|
if len(currentLine)+len(word)+1 > width {
|
||||||
|
if currentLine != "" {
|
||||||
|
lines = append(lines, currentLine)
|
||||||
|
currentLine = word
|
||||||
|
} else {
|
||||||
|
// Word is longer than width, break it
|
||||||
|
for len(word) > width {
|
||||||
|
lines = append(lines, word[:width])
|
||||||
|
word = word[width:]
|
||||||
|
}
|
||||||
|
currentLine = word
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if currentLine != "" {
|
||||||
|
currentLine += " " + word
|
||||||
|
} else {
|
||||||
|
currentLine = word
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentLine != "" {
|
||||||
|
lines = append(lines, currentLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
func min(a, b int) int {
|
func min(a, b int) int {
|
||||||
if a < b {
|
if a < b {
|
||||||
return a
|
return a
|
||||||
|
|||||||
Reference in New Issue
Block a user