Claude dapat menggunakan alat editor teks yang didefinisikan oleh Anthropic untuk melihat dan memodifikasi file teks, membantu Anda men-debug, memperbaiki, dan meningkatkan kode atau dokumen teks lainnya. Ini memungkinkan Claude untuk berinteraksi langsung dengan file Anda, memberikan bantuan langsung daripada hanya menyarankan perubahan.

Sebelum menggunakan alat editor teks

Gunakan model yang kompatibel

Alat editor teks Anthropic tersedia untuk beberapa model Claude:

  • Claude 4 Opus & Sonnet: text_editor_20250429
  • Claude Sonnet 3.7: text_editor_20250124
  • Claude Sonnet 3.5: text_editor_20241022

Versi terbaru text_editor_20250429 untuk model Claude 4 tidak menyertakan perintah undo_edit. Jika Anda memerlukan fungsionalitas ini, Anda perlu menggunakan Claude 3.7 atau Sonnet 3.5 dengan versi alat masing-masing.

Nilai kesesuaian kasus penggunaan Anda

Beberapa contoh kapan menggunakan alat editor teks adalah:

  • Debugging kode: Minta Claude untuk mengidentifikasi dan memperbaiki bug dalam kode Anda, mulai dari kesalahan sintaks hingga masalah logika.
  • Refactoring kode: Biarkan Claude meningkatkan struktur kode, keterbacaan, dan kinerja melalui pengeditan yang ditargetkan.
  • Pembuatan dokumentasi: Minta Claude untuk menambahkan docstring, komentar, atau file README ke basis kode Anda.
  • Pembuatan tes: Minta Claude untuk membuat unit test untuk kode Anda berdasarkan pemahamannya tentang implementasi.

Gunakan alat editor teks

Berikan alat editor teks (bernama str_replace_based_edit_tool) kepada Claude menggunakan Messages API:

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-20250514",
    max_tokens=1024,
    tools=[
        {
            "type": "text_editor_20250429",
            "name": "str_replace_based_edit_tool"
        }
    ],
    messages=[
        {
            "role": "user", 
            "content": "There's a syntax error in my primes.py file. Can you help me fix it?"
        }
    ]
)

Alat editor teks dapat digunakan dengan cara berikut:

1

Berikan Claude alat editor teks dan prompt pengguna

  • Sertakan alat editor teks dalam permintaan API Anda
  • Berikan prompt pengguna yang mungkin memerlukan pemeriksaan atau modifikasi file, seperti “Bisakah Anda memperbaiki kesalahan sintaks dalam kode saya?”
2

Claude menggunakan alat untuk memeriksa file atau direktori

  • Claude menilai apa yang perlu dilihat dan menggunakan perintah view untuk memeriksa isi file atau mencantumkan isi direktori
  • Respons API akan berisi blok konten tool_use dengan perintah view
3

Jalankan perintah view dan kembalikan hasilnya

  • Ekstrak jalur file atau direktori dari permintaan penggunaan alat Claude
  • Baca isi file atau daftar isi direktori dan kembalikan ke Claude
  • Kembalikan hasilnya ke Claude dengan melanjutkan percakapan dengan pesan user baru yang berisi blok konten tool_result
4

Claude menggunakan alat untuk memodifikasi file

  • Setelah memeriksa file atau direktori, Claude mungkin menggunakan perintah seperti str_replace untuk membuat perubahan atau insert untuk menambahkan teks pada nomor baris tertentu.
  • Jika Claude menggunakan perintah str_replace, Claude membuat permintaan penggunaan alat yang diformat dengan benar dengan teks lama dan teks baru untuk menggantinya
5

Jalankan pengeditan dan kembalikan hasilnya

  • Ekstrak jalur file, teks lama, dan teks baru dari permintaan penggunaan alat Claude
  • Lakukan penggantian teks dalam file
  • Kembalikan hasilnya ke Claude
6

Claude memberikan analisis dan penjelasannya

  • Setelah memeriksa dan mungkin mengedit file, Claude memberikan penjelasan lengkap tentang apa yang ditemukan dan perubahan apa yang dilakukan

Perintah alat editor teks

Alat editor teks mendukung beberapa perintah untuk melihat dan memodifikasi file:

view

Perintah view memungkinkan Claude memeriksa isi file atau mencantumkan isi direktori. Perintah ini dapat membaca seluruh file atau rentang baris tertentu.

Parameter:

  • command: Harus “view”
  • path: Jalur ke file atau direktori yang akan dilihat
  • view_range (opsional): Array dua bilangan bulat yang menentukan nomor baris awal dan akhir yang akan dilihat. Nomor baris dimulai dari 1, dan -1 untuk baris akhir berarti membaca hingga akhir file. Parameter ini hanya berlaku saat melihat file, bukan direktori.

str_replace

Perintah str_replace memungkinkan Claude mengganti string tertentu dalam file dengan string baru. Ini digunakan untuk membuat pengeditan yang tepat.

Parameter:

  • command: Harus “str_replace”
  • path: Jalur ke file yang akan dimodifikasi
  • old_str: Teks yang akan diganti (harus cocok persis, termasuk spasi dan indentasi)
  • new_str: Teks baru yang akan disisipkan sebagai pengganti teks lama

create

Perintah create memungkinkan Claude membuat file baru dengan konten yang ditentukan.

Parameter:

  • command: Harus “create”
  • path: Jalur tempat file baru harus dibuat
  • file_text: Konten yang akan ditulis ke file baru

insert

Perintah insert memungkinkan Claude menyisipkan teks di lokasi tertentu dalam file.

Parameter:

  • command: Harus “insert”
  • path: Jalur ke file yang akan dimodifikasi
  • insert_line: Nomor baris setelah teks akan disisipkan (0 untuk awal file)
  • new_str: Teks yang akan disisipkan

undo_edit

Perintah undo_edit memungkinkan Claude membatalkan pengeditan terakhir yang dilakukan pada file.

Perintah ini hanya tersedia di Claude Sonnet 3.7 dan Claude Sonnet 3.5. Perintah ini tidak didukung di model Claude 4 yang menggunakan text_editor_20250429.

Parameter:

  • command: Harus “undo_edit”
  • path: Jalur ke file yang pengeditan terakhirnya harus dibatalkan

Contoh: Memperbaiki kesalahan sintaks dengan alat editor teks

Contoh ini menunjukkan bagaimana Claude 4 menggunakan alat editor teks untuk memperbaiki kesalahan sintaks dalam file Python.

Pertama, aplikasi Anda memberikan Claude alat editor teks dan prompt untuk memperbaiki kesalahan sintaks:

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-4-20250514",
    max_tokens=1024,
    tools=[
        {
            "type": "text_editor_20250429",
            "name": "str_replace_based_edit_tool"
        }
    ],
    messages=[
        {
            "role": "user", 
            "content": "There's a syntax error in my primes.py file. Can you help me fix it?"
        }
    ]
)

print(response)

Nomor baris

Dalam contoh di atas, hasil alat view menyertakan isi file dengan nomor baris yang ditambahkan ke setiap baris (misalnya, “1: def is_prime(n):”). Nomor baris tidak diperlukan, tetapi sangat penting untuk berhasil menggunakan parameter view_range untuk memeriksa bagian tertentu dari file dan parameter insert_line untuk menambahkan konten di lokasi yang tepat.

Claude akan menggunakan alat editor teks terlebih dahulu untuk melihat file:

{
  "id": "msg_01XAbCDeFgHiJkLmNoPQrStU",
  "model": "claude-opus-4-20250514",
  "stop_reason": "tool_use",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "I'll help you fix the syntax error in your primes.py file. First, let me take a look at the file to identify the issue."
    },
    {
      "type": "tool_use",
      "id": "toolu_01AbCdEfGhIjKlMnOpQrStU",
      "name": "str_replace_based_edit_tool",
      "input": {
        "command": "view",
        "path": "primes.py"
      }
    }
  ]
}

Aplikasi Anda kemudian harus membaca file dan mengembalikan isinya ke Claude:

response = client.messages.create(
    model="claude-opus-4-20250514",
    max_tokens=1024,
    tools=[
        {
            "type": "text_editor_20250429",
            "name": "str_replace_based_edit_tool"
        }
    ],
    messages=[
        # Previous messages...
        {
            "role": "assistant",
            "content": [
                {
                    "type": "text",
                    "text": "I'll help you fix the syntax error in your primes.py file. First, let me take a look at the file to identify the issue."
                },
                {
                    "type": "tool_use",
                    "id": "toolu_01AbCdEfGhIj�# Anurag-Bharati/RootNodeApp
# app/src/main/java/com/rootnode/rnapp/ui/screens/auth/AuthScreen.kt
package com.rootnode.rnapp.ui.screens.auth

import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import com.rootnode.rnapp.R
import com.rootnode.rnapp.ui.theme.RNAppTheme
import com.rootnode.rnapp.ui.theme.poppinsFamily
import com.rootnode.rnapp.ui.theme.primaryColor
import com.rootnode.rnapp.ui.theme.secondaryColor
import com.rootnode.rnapp.ui.theme.textFieldBg
import com.rootnode.rnapp.ui.theme.textFieldBgDark
import com.rootnode.rnapp.ui.theme.textFieldBorderColor
import com.rootnode.rnapp.ui.theme.textFieldBorderColorDark
import com.rootnode.rnapp.ui.theme.textFieldTextColor
import com.rootnode.rnapp.ui.theme.textFieldTextColorDark
import com.rootnode.rnapp.ui.theme.textFieldTextHintColor
import com.rootnode.rnapp.ui.theme.textFieldTextHintColorDark
import com.rootnode.rnapp.ui.theme.textFieldTextLabelColor
import com.rootnode.rnapp.ui.theme.textFieldTextLabelColorDark
import com.rootnode.rnapp.ui.viewmodel.AuthViewModel
import com.rootnode.rnapp.utils.AuthState
import kotlinx.coroutines.launch

@Composable
fun AuthScreen(
    viewModel: AuthViewModel = hiltViewModel(),
    onNavigateToHome: () -> Unit,
) {
    val authState by viewModel.authState.collectAsState()
    val snackbarHostState = remember { SnackbarHostState() }
    val scope = rememberCoroutineScope()
    var isLogin by remember { mutableStateOf(true) }

    LaunchedEffect(authState) {
        when (authState) {
            is AuthState.Success -> {
                onNavigateToHome()
            }

            is AuthState.Error -> {
                scope.launch {
                    snackbarHostState.showSnackbar(
                        (authState as AuthState.Error).message
                    )
                }
            }

            else -> {}
        }
    }

    Scaffold(
        snackbarHost = { SnackbarHost(snackbarHostState) }
    ) { paddingValues ->
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues)
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .verticalScroll(rememberScrollState())
                    .padding(16.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                Image(
                    painter = painterResource(id = R.drawable.logo),
                    contentDescription = "Logo",
                    modifier = Modifier
                        .size(120.dp)
                        .padding(bottom = 16.dp)
                )

                Text(
                    text = if (isLogin) "Login to your account" else "Create a new account",
                    fontFamily = poppinsFamily,
                    fontWeight = FontWeight.Bold,
                    fontSize = 24.sp,
                    textAlign = TextAlign.Center,
                    modifier = Modifier.padding(bottom = 24.dp)
                )

                AnimatedVisibility(
                    visible = isLogin,
                    enter = fadeIn(animationSpec = tween(300)) + slideInVertically(
                        animationSpec = tween(300),
                        initialOffsetY = { it }
                    ),
                    exit = fadeOut(animationSpec = tween(300)) + slideOutVertically(
                        animationSpec = tween(300),
                        targetOffsetY = { it }
                    )
                ) {
                    LoginForm(
                        email = viewModel.email,
                        password = viewModel.password,
                        onEmailChange = { viewModel.email = it },
                        onPasswordChange = { viewModel.password = it },
                        onLogin = { viewModel.login() },
                        isLoading = authState is AuthState.Loading
                    )
                }

                AnimatedVisibility(
                    visible = !isLogin,
                    enter = fadeIn(animationSpec = tween(300)) + slideInVertically(
                        animationSpec = tween(300),
                        initialOffsetY = { it }
                    ),
                    exit = fadeOut(animationSpec = tween(300)) + slideOutVertically(
                        animationSpec = tween(300),
                        targetOffsetY = { it }
                    )
                ) {
                    RegisterForm(
                        name = viewModel.name,
                        email = viewModel.email,
                        password = viewModel.password,
                        confirmPassword = viewModel.confirmPassword,
                        onNameChange = { viewModel.name = it },
                        onEmailChange = { viewModel.email = it },
                        onPasswordChange = { viewModel.password = it },
                        onConfirmPasswordChange = { viewModel.confirmPassword = it },
                        onRegister = { viewModel.register() },
                        isLoading = authState is AuthState.Loading
                    )
                }

                Spacer(modifier = Modifier.height(16.dp))

                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text(
                        text = if (isLogin) "Don't have an account? " else "Already have an account? ",
                        fontFamily = poppinsFamily,
                        fontSize = 14.sp
                    )
                    Text(
                        text = if (isLogin) "Register" else "Login",
                        fontFamily = poppinsFamily,
                        fontWeight = FontWeight.Bold,
                        fontSize = 14.sp,
                        color = primaryColor,
                        modifier = Modifier.clickable { isLogin = !isLogin }
                    )
                }

                Spacer(modifier = Modifier.height(24.dp))

                Row(
                    modifier = Modifier.fillMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Divider(
                        modifier = Modifier
                            .weight(1f)
                            .height(1.dp)
                    )
                    Text(
                        text = "OR",
                        fontFamily = poppinsFamily,
                        fontSize = 14.sp,
                        modifier = Modifier.padding(horizontal = 16.dp)
                    )
                    Divider(
                        modifier = Modifier
                            .weight(1f)
                            .height(1.dp)
                    )
                }

                Spacer(modifier = Modifier.height(24.dp))

                SocialLoginButtons()
            }

            if (authState is AuthState.Loading) {
                Box(
                    modifier = Modifier
                        .fillMaxSize()
                        .background(Color.Black.copy(alpha = 0.5f)),
                    contentAlignment = Alignment.Center
                ) {
                    CircularProgressIndicator(color = primaryColor)
                }
            }
        }
    }
}

@Composable
fun LoginForm(
    email: String,
    password: String,
    onEmailChange: (String) -> Unit,
    onPasswordChange: (String) -> Unit,
    onLogin: () -> Unit,
    isLoading: Boolean
) {
    val focusManager = LocalFocusManager.current
    var passwordVisible by remember { mutableStateOf(false) }

    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        CustomTextField(
            value = email,
            onValueChange = onEmailChange,
            label = "Email",
            placeholder = "Enter your email",
            leadingIcon = {
                Icon(
                    imageVector = Icons.Default.Email,
                    contentDescription = "Email Icon"
                )
            },
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Email,
                imeAction = ImeAction.Next
            ),
            keyboardActions = KeyboardActions(
                onNext = { focusManager.moveFocus(FocusDirection.Down) }
            ),
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(16.dp))

        CustomTextField(
            value = password,
            onValueChange = onPasswordChange,
            label = "Password",
            placeholder = "Enter your password",
            leadingIcon = {
                Icon(
                    imageVector = Icons.Default.Lock,
                    contentDescription = "Password Icon"
                )
            },
            trailingIcon = {
                IconButton(onClick = { passwordVisible = !passwordVisible }) {
                    Icon(
                        imageVector = if (passwordVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff,
                        contentDescription = if (passwordVisible) "Hide password" else "Show password"
                    )
                }
            },
            visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Password,
                imeAction = ImeAction.Done
            ),
            keyboardActions = KeyboardActions(
                onDone = {
                    focusManager.clearFocus()
                    if (!isLoading) onLogin()
                }
            ),
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(8.dp))

        Text(
            text = "Forgot Password?",
            fontFamily = poppinsFamily,
            fontWeight = FontWeight.Medium,
            fontSize = 14.sp,
            color = primaryColor,
            textAlign = TextAlign.End,
            modifier = Modifier
                .fillMaxWidth()
                .padding(end = 8.dp)
                .clickable { /* Handle forgot password */ }
        )

        Spacer(modifier = Modifier.height(24.dp))

        Button(
            onClick = { if (!isLoading) onLogin() },
            modifier = Modifier
                .fillMaxWidth()
                .height(56.dp),
            shape = RoundedCornerShape(12.dp),
            colors = ButtonDefaults.buttonColors(
                containerColor = primaryColor,
                contentColor = Color.White
            ),
            enabled = !isLoading
        ) {
            Text(
                text = "Login",
                fontFamily = poppinsFamily,
                fontWeight = FontWeight.Bold,
                fontSize = 16.sp
            )
        }
    }
}

@Composable
fun RegisterForm(
    name: String,
    email: String,
    password: String,
    confirmPassword: String,
    onNameChange: (String) -> Unit,
    onEmailChange: (String) -> Unit,
    onPasswordChange: (String) -> Unit,
    onConfirmPasswordChange: (String) -> Unit,
    onRegister: () -> Unit,
    isLoading: Boolean
) {
    val focusManager = LocalFocusManager.current
    var passwordVisible by remember { mutableStateOf(false) }
    var confirmPasswordVisible by remember { mutableStateOf(false) }

    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        CustomTextField(
            value = name,
            onValueChange = onNameChange,
            label = "Full Name",
            placeholder = "Enter your full name",
            leadingIcon = {
                Icon(
                    imageVector = Icons.Default.Person,
                    contentDescription = "Name Icon"
                )
            },
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Text,
                imeAction = ImeAction.Next
            ),
            keyboardActions = KeyboardActions(
                onNext = { focusManager.moveFocus(FocusDirection.Down) }
            ),
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(16.dp))

        CustomTextField(
            value = email,
            onValueChange = onEmailChange,
            label = "Email",
            placeholder = "Enter your email",
            leadingIcon = {
                Icon(
                    imageVector = Icons.Default.Email,
                    contentDescription = "Email Icon"
                )
            },
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Email,
                imeAction = ImeAction.Next
            ),
            keyboardActions = KeyboardActions(
                onNext = { focusManager.moveFocus(FocusDirection.Down) }
            ),
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(16.dp))

        CustomTextField(
            value = password,
            onValueChange = onPasswordChange,
            label = "Password",
            placeholder = "Enter your password",
            leadingIcon = {
                Icon(
                    imageVector = Icons.Default.Lock,
                    contentDescription = "Password Icon"
                )
            },
            trailingIcon = {
                IconButton(onClick = { passwordVisible = !passwordVisible }) {
                    Icon(
                        imageVector = if (passwordVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff,
                        contentDescription = if (passwordVisible) "Hide password" else "Show password"
                    )
                }
            },
            visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Password,
                imeAction = ImeAction.Next
            ),
            keyboardActions = KeyboardActions(
                onNext = { focusManager.moveFocus(FocusDirection.Down) }
            ),
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(16.dp))

        CustomTextField(
            value = confirmPassword,
            onValueChange = onConfirmPasswordChange,
            label = "Confirm Password",
            placeholder = "Confirm your password",
            leadingIcon = {
                Icon(
                    imageVector = Icons.Default.Lock,
                    contentDescription = "Confirm Password Icon"
                )
            },
            trailingIcon = {
                IconButton(onClick = { confirmPasswordVisible = !confirmPasswordVisible }) {
                    Icon(
                        imageVector = if (confirmPasswordVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff,
                        contentDescription = if (confirmPasswordVisible) "Hide password" else "Show password"
                    )
                }
            },
            visualTransformation = if (confirmPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Password,
                imeAction = ImeAction.Done
            ),
            keyboardActions = KeyboardActions(
                onDone = {
                    focusManager.clearFocus()
                    if (!isLoading) onRegister()
                }
            ),
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(24.dp))

        Button(
            onClick = { if (!isLoading) onRegister() },
            modifier = Modifier
                .fillMaxWidth()
                .height(56.dp),
            shape = RoundedCornerShape(12.dp),
            colors = ButtonDefaults.buttonColors(
                containerColor = primaryColor,
                contentColor = Color.White
            ),
            enabled = !isLoading
        ) {
            Text(
                text = "Register",
                fontFamily = poppinsFamily,
                fontWeight = FontWeight.Bold,
                fontSize = 16.sp
            )
        }
    }
}

@Composable
fun CustomTextField(
    value: String,
    onValueChange: (String) -> Unit,
    label: String,
    placeholder: String,
    leadingIcon: @Composable (() -> Unit)? = null,
    trailingIcon: @Composable (() -> Unit)? = null,
    visualTransformation: VisualTransformation = VisualTransformation.None,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions.Default,
    modifier: Modifier = Modifier
) {
    val isDarkTheme = MaterialTheme.colorScheme.isFromPalette

    OutlinedTextField(
        value = value,
        onValueChange = onValueChange,
        label = { Text(text = label, fontFamily = poppinsFamily) },
        placeholder = { Text(text = placeholder, fontFamily = poppinsFamily) },
        leadingIcon = leadingIcon,
        trailingIcon = trailingIcon,
        visualTransformation = visualTransformation,
        keyboardOptions = keyboardOptions,
        keyboardActions = keyboardActions,
        singleLine = true,
        shape = RoundedCornerShape(12.dp),
        colors = OutlinedTextFieldDefaults.colors(
            focusedTextColor = if (isDarkTheme) textFieldTextColorDark else textFieldTextColor,
            unfocusedTextColor = if (isDarkTheme) textFieldTextColorDark else textFieldTextColor,
            focusedContainerColor = if (isDarkTheme) textFieldBgDark else textFieldBg,
            unfocusedContainerColor = if (isDarkTheme) textFieldBgDark else textFieldBg,
            focusedBorderColor = if (isDarkTheme) textFieldBorderColorDark else textFieldBorderColor,
            unfocusedBorderColor = if (isDarkTheme) textFieldBorderColorDark else textFieldBorderColor,
            focusedLabelColor = if (isDarkTheme) textFieldTextLabelColorDark else textFieldTextLabelColor,
            unfocusedLabelColor = if (isDarkTheme) textFieldTextLabelColorDark else textFieldTextLabelColor,
            focusedPlaceholderColor = if (isDarkTheme) textFieldTextHintColorDark else textFieldTextHintColor,
            unfocusedPlaceholderColor = if (isDarkTheme) textFieldTextHintColorDark else textFieldTextHintColor,
        ),
        modifier = modifier
    )
}

@Composable
fun SocialLoginButtons() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        SocialButton(
            icon = R.drawable.ic_google,
            onClick = { /* Handle Google login */ }
        )

        Spacer(modifier = Modifier.width(16.dp))

        SocialButton(
            icon = R.drawable.ic_facebook,
            onClick = { /* Handle Facebook login */ }
        )

        Spacer(modifier = Modifier.width(16.dp))

        SocialButton(
            icon = R.drawable.ic_apple,
            onClick = { /* Handle Apple login */ }
        )
    }
}

@Composable
fun SocialButton(
    icon: Int,
    onClick: () -> Unit
) {
    Box(
        modifier = Modifier
            .size(56.dp)
            .clip(RoundedCornerShape(12.dp))
            .background(MaterialTheme.colorScheme.surface)
            .clickable(onClick = onClick),
        contentAlignment = Alignment.Center
    ) {
        Image(
            painter = painterResource(id = icon),
            contentDescription = "Social Login",
            modifier = Modifier.size(24.dp)
        )
    }
}

@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun AuthScreenPreview() {
    RNAppTheme {
        Surface(
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight(),
            color = MaterialTheme.colorScheme.background
        ) {
            AuthScreen(onNavigateToHome = {})
        }
    }
}
KlMnOpQrStU",
                    "name": "str_replace_based_edit_tool",
                    "input": {
                        "command": "view",
                        "path": "primes.py"
                    }
                }
            ]
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "toolu_01AbCdEfGhIjKlMnOpQrStU",
                    "content": "1: def is_prime(n):\n2:     \"\"\"Check if a number is prime.\"\"\"\n3:     if n <= 1:\n4:         return False\n5:     if n <= 3:\n6:         return True\n7:     if n % 2 == 0 or n % 3 == 0:\n8:         return False\n9:     i = 5\n10:     while i * i <= n:\n11:         if n % i == 0 or n % (i + 2) == 0:\n12:             return False\n13:         i += 6\n14:     return True\n15: \n16: def get_primes(limit):\n17:     \"\"\"Generate a list of prime numbers up to the given limit.\"\"\"\n18:     primes = []\n19:     for num in range(2, limit + 1)\n20:         if is_prime(num):\n21:             primes.append(num)\n22:     return primes\n23: \n24: def main():\n25:     \"\"\"Main function to demonstrate prime number generation.\"\"\"\n26:     limit = 100\n27:     prime_list = get_primes(limit)\n28:     print(f\"Prime numbers up to {limit}:\")\n29:     print(prime_list)\n30:     print(f\"Found {len(prime_list)} prime numbers.\")\n31: \n32: if __name__ == \"__main__\":\n33:     main()"
                }
            ]
        }
    ]
)

Claude akan mengidentifikasi kesalahan sintaks dan menggunakan perintah str_replace untuk memperbaikinya:

{
  "id": "msg_01VwXyZAbCdEfGhIjKlMnO",
  "model": "claude-opus-4-20250514",
  "stop_reason": "tool_use",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "I found the syntax error in your primes.py file. In the `get_primes` function, there is a missing colon (:) at the end of the for loop line. Let me fix that for you."
    },
    {
      "type": "tool_use",
      "id": "toolu_01PqRsTuVwXyZAbCdEfGh",
      "name": "str_replace_based_edit_tool",
      "input": {
        "command": "str_replace",
        "path": "primes.py",
        "old_str": "    for num in range(2, limit + 1)",
        "new_str": "    for num in range(2, limit + 1):"
      }
    }
  ]
}

Aplikasi Anda kemudian harus membuat pengeditan dan mengembalikan hasilnya:

response = client.messages.create(
    model="claude-opus-4-20250514",
    max_tokens=1024,
    tools=[
        {
            "type": "text_editor_20250429",
            "name": "str_replace_based_edit_tool"
        }
    ],
    messages=[
        # Previous messages...
        {
            "role": "assistant",
            "content": [
                {
                    "type": "text",
                    "text": "I found the syntax error in your primes.py file. In the `get_primes` function, there is a missing colon (:) at the end of the for loop line. Let me fix that for you."
                },
                {
                    "type": "tool_use",
                    "id": "toolu_01PqRsTuVwXyZAbCdEfGh",
                    "name": "str_replace_based_edit_tool",
                    "input": {
                        "command": "str_replace",
                        "path": "primes.py",
                        "old_str": "    for num in range(2, limit + 1)",
                        "new_str": "    for num in range(2, limit + 1):"
                    }
                }
            ]
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "toolu_01PqRsTuVwXyZAbCdEfGh",
                    "content": "Successfully replaced text at exactly one location."
                }
            ]
        }
    ]
)

Akhirnya, Claude akan memberikan penjelasan lengkap tentang perbaikan:

{
  "id": "msg_01IjKlMnOpQrStUvWxYzAb",
  "model": "claude-opus-4-20250514",
  "stop_reason": "end_turn",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "I've fixed the syntax error in your primes.py file. The issue was in the `get_primes` function at line 19. There was a missing colon (:) at the end of the for loop line.\n\nHere's what I changed:\n\nFrom:\n```python\nfor num in range(2, limit + 1)\n```\n\nTo:\n```python\nfor num in range(2, limit + 1):\n```\n\nIn Python, a colon is required at the end of statements that start a new code block, such as for loops, if statements, function definitions, etc. Without the colon, Python raises a syntax error because it doesn't know where the code block begins.\n\nYour code should now run correctly. The function will properly generate a list of prime numbers up to the specified limit."
    }
  ]
}

Implementasikan alat editor teks

Alat editor teks diimplementasikan sebagai alat tanpa skema. Saat menggunakan alat ini, Anda tidak perlu menyediakan skema input seperti dengan alat lain; skema sudah tertanam dalam model Claude dan tidak dapat dimodifikasi.

Tipe alat tergantung pada versi model:

  • Claude 4: type: "text_editor_20250429"
  • Claude Sonnet 3.7: type: "text_editor_20250124"
  • Claude Sonnet 3.5: type: "text_editor_20241022"
1

Inisialisasi implementasi editor Anda

Buat fungsi pembantu untuk menangani operasi file seperti membaca, menulis, dan memodifikasi file. Pertimbangkan untuk mengimplementasikan fungsionalitas backup untuk memulihkan dari kesalahan.

2

Tangani panggilan alat editor

Buat fungsi yang memproses panggilan alat dari Claude berdasarkan jenis perintah:

def handle_editor_tool(tool_call, model_version):
    input_params = tool_call.input
    command = input_params.get('command', '')
    file_path = input_params.get('path', '')
    
    if command == 'view':
        # Baca dan kembalikan isi file
        pass
    elif command == 'str_replace':
        # Ganti teks dalam file
        pass
    elif command == 'create':
        # Buat file baru
        pass
    elif command == 'insert':
        # Sisipkan teks di lokasi
        pass
    elif command == 'undo_edit':
        # Periksa apakah ini model Claude 4
        if 'str_replace_based_edit_tool' in model_version:
            return {"error": "undo_edit command is not supported in Claude 4"}
        # Pulihkan dari backup untuk Claude 3.7/3.5
        pass
3

Implementasikan langkah-langkah keamanan

Tambahkan validasi dan pemeriksaan keamanan:

  • Validasi jalur file untuk mencegah traversal direktori
  • Buat backup sebelum membuat perubahan
  • Tangani kesalahan dengan anggun
  • Implementasikan pemeriksaan izin
4

Proses respons Claude

Ekstrak dan tangani panggilan alat dari respons Claude:

# Proses penggunaan alat dalam respons Claude
for content in response.content:
    if content.type == "tool_use":
        # Jalankan alat berdasarkan perintah
        result = handle_editor_tool(content)
        
        # Kembalikan hasil ke Claude
        tool_result = {
            "type": "tool_result",
            "tool_use_id": content.id,
            "content": result
        }

Saat mengimplementasikan alat editor teks, perhatikan:

  1. Keamanan: Alat ini memiliki akses ke sistem file lokal Anda, jadi terapkan langkah-langkah keamanan yang tepat.
  2. Backup: Selalu buat backup sebelum mengizinkan pengeditan pada file penting.
  3. Validasi: Validasi semua input untuk mencegah perubahan yang tidak diinginkan.
  4. Pencocokan unik: Pastikan penggantian cocok persis di satu lokasi untuk menghindari pengeditan yang tidak diinginkan.

Tangani kesalahan

Saat menggunakan alat editor teks, berbagai kesalahan mungkin terjadi. Berikut adalah panduan tentang cara menanganinya:

Ikuti praktik terbaik implementasi


Harga dan penggunaan token

Alat editor teks menggunakan struktur harga yang sama dengan alat lain yang digunakan dengan Claude. Ini mengikuti harga token input dan output standar berdasarkan model Claude yang Anda gunakan.

Selain token dasar, token input tambahan berikut diperlukan untuk alat editor teks:

AlatToken input tambahan
text_editor_20250429 (Claude 4)700 token
text_editor_20250124 (Claude Sonnet 3.7)700 token
text_editor_20241022 (Claude Sonnet 3.5)700 token

Untuk informasi lebih rinci tentang harga alat, lihat Harga penggunaan alat.

Integrasikan alat editor teks dengan penggunaan komputer

Alat editor teks dapat digunakan bersama dengan alat penggunaan komputer dan alat lain yang didefinisikan oleh Anthropic. Saat menggabungkan alat-alat ini, Anda perlu:

  1. Sertakan header beta yang sesuai (jika digunakan dengan penggunaan komputer)
  2. Cocokkan versi al at dengan model yang Anda gunakan
  3. Perhitungkan penggunaan token tambahan untuk semua alat yang disertakan dalam permintaan Anda

Untuk informasi lebih lanjut tentang penggunaan alat editor teks dalam konteks penggunaan komputer, lihat Penggunaan komputer.

Log perubahan

TanggalVersiPerubahan
29 April 2025text_editor_20250429Rilis Alat editor teks untuk Claude 4. Versi ini menghapus perintah undo_edit tetapi mempertahankan semua kemampuan lainnya. Nama alat telah diperbarui untuk mencerminkan arsitektur berbasis str_replace.
13 Maret 2025text_editor_20250124Pengenalan dokumentasi Alat editor teks mandiri. Versi ini dioptimalkan untuk Claude Sonnet 3.7 tetapi memiliki kemampuan yang identik dengan versi sebelumnya.
22 Oktober 2024text_editor_20241022Rilis awal Alat editor teks dengan Claude Sonnet 3.5. Menyediakan kemampuan untuk melihat, membuat, dan mengedit file melalui perintah view, create, str_replace, insert, dan undo_edit.

Langkah selanjutnya

Berikut adalah beberapa ide tentang cara menggunakan alat editor teks dengan cara yang lebih nyaman dan kuat:

  • Integrasikan dengan alur kerja pengembangan Anda: Bangun alat editor teks ke dalam alat pengembangan atau IDE Anda
  • Buat sistem peninjauan kode: Minta Claude meninjau kode Anda dan membuat perbaikan
  • Bangun asisten debugging: Buat sistem di mana Claude dapat membantu Anda mendiagnosis dan memperbaiki masalah dalam kode Anda
  • Implementasikan konversi format file: Biarkan Claude membantu Anda mengonversi file dari satu format ke format lain
  • Otomatisasi dokumentasi: Siapkan alur kerja agar Claude secara otomatis mendokumentasikan kode Anda

Saat Anda membangun aplikasi dengan alat editor teks, kami sangat senang melihat bagaimana Anda memanfaatkan kemampuan Claude untuk meningkatkan alur kerja dan produktivitas pengembangan Anda.